poj 3264 Balanced Lineup(线段树)

Balanced Lineup

Time Limit: 5000MS   Memory Limit: 65536K
Total Submissions: 64371   Accepted: 30005
Case Time Limit: 2000MS

Description

For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up in the same order. One day Farmer John decides to organize a game of Ultimate Frisbee with some of the cows. To keep things simple, he will take a contiguous range of cows from the milking lineup to play the game. However, for all the cows to have fun they should not differ too much in height.

Farmer John has made a list of Q (1 ≤ Q ≤ 200,000) potential groups of cows and their heights (1 ≤ height ≤ 1,000,000). For each group, he wants your help to determine the difference in height between the shortest and the tallest cow in the group.

Input

Line 1: Two space-separated integers, N and Q
Lines 2..N+1: Line i+1 contains a single integer that is the height of cow i 
Lines N+2..N+Q+1: Two integers A and B (1 ≤ A ≤ B ≤ N), representing the range of cows from A to B inclusive.

Output

Lines 1..Q: Each line contains a single integer that is a response to a reply and indicates the difference in height between the tallest and shortest cow in the range.

Sample Input

6 3
1
7
3
4
2
5
1 5
4 6
2 2

Sample Output

6
3
0

给定Q (1 ≤ Q ≤ 200,000)个数A1,A2 … AQ,,多次求任一区间Ai – Aj中最大数和最小数的差。

 

一般情况会想到直接查找运算。但q个数操作一次时间复杂度为O(q),查找n次,并当n很大时,nO(q)时间复杂度就偏大,因此,想到用线段树。

线段树是一个完全二叉树,树中的每一个结点表示了一个区间[a,b]。a,b通常是整数。每一个叶子节点表示了一个单位区间(长度为1)。对于每一个非叶结点所表示的结点[a,b],其左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b](除法去尾取整)。查找一次的时间复杂度为O(logn),n次就是O(nlog(n)).

利用线段树解题的一般步骤:

先建树,再插入数据,然后更新,查询。

poj 3264 Balanced Lineup(线段树)_第1张图片

 该题求最大值最小值之差,则:

poj 3264 Balanced Lineup(线段树)_第2张图片

#include
#include
#include
const int inf=0xffffff0;
using namespace std;
struct node{
    int l;
    int r;
    int minx,maxx;
}tree[800010];//叶子结点有n个,那总结点为2n-1
int mid;
int minx=inf;
int maxx=-inf;
void create(int root,int li,int ri){//建线段树,并将每个节点的最大值最小值初始化
    tree[root].l=li;
    tree[root].r=ri;
    tree[root].minx=inf;
    tree[root].maxx=-inf;
    if(tree[root].l!=tree[root].r){
        mid =(li+ri)/2;
        create(2*root+1,li,mid);
        create(2*root+2,mid+1,ri);
    }
}
void Insert(int root,int i,int h){//从root开始,将第i个值为h的数插入线段树
   if(tree[root].l==tree[root].r){
    tree[root].maxx=tree[root].minx=h;
    return;
   }
   tree[root].minx=min(tree[root].minx,h);
   tree[root].maxx=max(tree[root].maxx,h);
   if(i<=(tree[root].l+tree[root].r)/2)//编号在root的左边
       Insert(2*root+1,i,h);
   else
       Insert(2*root+2,i,h);
}
void query(int root,int a1,int a2){
    if(tree[root].minx>=minx&&tree[root].maxx<=maxx)
        return;//剪枝,如果结点区间内的最大值已经比之前求出的最大值还小且最小值比之前求出的最小值还大,则没必要再分下去了。
    if(tree[root].l==a1&&tree[root].r==a2){
        minx=min(minx,tree[root].minx);
        maxx=max(maxx,tree[root].maxx);
        return;
    }
    mid=(tree[root].l+tree[root].r)/2;
    if(a2<=mid){//若所找区间完全在root的左边,去左半边找区间(a1,a2)
        query(2*root+1,a1,a2);
    }
    else if(a1>mid){
        query(2*root+2,a1,a2);
    }
    else{
        query(2*root+1,a1,mid);
        query(2*root+2,mid+1,a2);
    }

}
int main(){
    int n,p,h;
    int a1,a2;
    scanf("%d%d",&n,&p);
    create(0,1,n);
    for(int i=1;i<=n;i++){
        scanf("%d",&h);
        Insert(0,i,h);
    }
    for(int i=0;i

 

你可能感兴趣的:(线段树)