10.25算法训练——裸线段树

题目大意:对N(1<=N<=50000)个数进行连续进行M(1<=M<=200000)次询问:问1-N之间任意连续区间最大值和最小值之差。

之前学过线段树,学的是模版题,求解的问题是在一段区间内任意加减,然后再询问任意一段之区间的和。

这次的问题和之前学的模版题相同之处是:查询的是一段连续区间的信息。

不同之处在于:区间求和问题需要在线段树的每个结点记录其左儿子和右儿子所有结点之和。也就是说每个结点的信息是一个数。

所以之后的查询操作,也是不断去访问线段树的结点,将这些结点上的数加起来即可。   

这次的问题是需要知道任意区间的最大值和最小值,因此,每个结点上应该存放的信息是:左儿子和右儿子管辖区间所有数的最大值和最小值。

注意:这里不能把结点信息设计为:左儿子max或min和右儿子max或min的差。因为左儿子管辖区间的最大值最小值之差和右儿子管辖区间

最大值最小值差  不一定是整个区间的最大值最小值之差,也就是说,不满足“区间可加性”。  

一旦设计好每个结点应该存放的信息(也就是两个数 max和min ),每次查询到根结点时更新当前的最大值最小值,查询完所有结点后max-min就是答案。

#include<bits/stdc++.h>
using namespace std;
struct node{
    int max,min;
};
const int maxl=50000;
node Sum[maxl<<2];
int A[maxl];
int MAX,MIN;
 
void PushUp(int rt){
     Sum[rt].max=max(Sum[rt<<1].max, Sum[rt<<1|1].max);
      Sum[rt].min=min(Sum[rt<<1].min, Sum[rt<<1|1].min);
 }  

void Build(int l,int r,int rt)
{ 
    if(l==r) {
        Sum[rt].max=A[l];
        Sum[rt].min=A[l];
        return;  
    }  
    int m=(l+r)>>1;    
    Build(l,m,rt<<1);  
    Build(m+1,r,rt<<1|1);    
    PushUp(rt);  
}
 
void Query(int L,int R,int l,int r,int rt)
{  
    if(L <= l && r <= R){
        MAX=max(MAX, Sum[rt].max);
        MIN=min(MIN, Sum[rt].min);
        return;
    }
    int m=(l+r)>>1;    
    if(L <= m) Query(L,R,l,m,rt<<1);  
    if(R >  m) Query(L,R,m+1,r,rt<<1|1);   
}

int main()
{
    int N,M;
    while(scanf("%d%d",&N,&M)!=EOF)
    {
        for(int i=1;i<=N;++i)
            scanf("%d",&A[i]);
        Build(1,N,1);
        for(int i=0;i<M;i++)
        {
            MAX=-1, MIN=1e7;
            int a,b;
            scanf("%d%d",&a,&b);
            Query(a,b,1,N,1);
            cout << MAX-MIN << endl;
        }
    }
    return 0;
}

 

posted @ 2018-10-27 22:14  AyaHiro  阅读(...)  评论(...编辑  收藏