10.25算法训练——裸线段树

Posted bxy0516

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}

 

以上是关于10.25算法训练——裸线段树的主要内容,如果未能解决你的问题,请参考以下文章

算法训练 操作格子 线段树板子题

BZOJ1798题解 Seq维护序列题解 双tag裸线段树

线段树合并

线段树基础

HDU 1166 敌兵布阵(线段树点更新区间求和裸题)

2018年全国多校算法寒假训练营练习比赛(第五场)题解