初识线段树

来源:互联网 发布:内存卡数据恢复软件 编辑:程序博客网 时间:2024/06/14 01:57

序列操作

【问题描述】

  给出一个有n个元素的的数组:A[1],A[2],…,A[n],你的任务是设计一个数据结构,支持以下三种操作:
  1 L R v:把A[L],A[L+1],…A[R]都增加v(v>=0)。
  2 L R v:把A[L],A[L+1],…A[R]都修改成(v>=0)。
  3 L R:计算A[L],A[L+1],…A[R]的元素和、最小值和最大值。
  输入保证任意时刻序列中所有元素和不超过10^9。
  
【输入格式】

  第一行包含 2 个正整数:n 和 m 。以下n行,每行一个整数,表示 A[i]。再以下 m 行,每行为上述三种操作之一。

【输出格式】

  针对操作类型3,依次输出元素和、最小值和最大值。

【输入样例】

10 7
1 2 3 4 5 6 7 8 9 10
3 4 8
1 3 7 2
2 4 8 5
3 3 7
1 6 9 1
2 3 5 0
3 4 7

【输出样例】
30 4 8
25 5 5
12 0 6

【数据范围】

1<=m,n<=100 000。
最初始的|A[i]|<=100。


#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>#include<cmath>#include<stack>#include<queue>#include<set>#include<map>#include<vector>#include<cctype>using namespace std;const int maxn=100005;int minv[2*maxn],np,rt,maxv[2*maxn],sum[2*maxn],add[2*maxn],setv[2*maxn],a[maxn],lc[2*maxn],rc[2*maxn];int n,m,ch,x,y,d;void initial(){    rt=np=0;    for(int i=1;i<=2*n;i++) setv[i]=-1;    memset(lc,0,sizeof(lc));    memset(rc,0,sizeof(rc));}void pushup(int now){    minv[now]=min(minv[lc[now]],minv[rc[now]]);    maxv[now]=max(maxv[lc[now]],maxv[rc[now]]);    sum[now]=sum[lc[now]]+sum[rc[now]];}void pushdowncha(int now,int L,int R){    if(setv[now]>=0)    {        int m=(L+R)>>1;        add[lc[now]]=0;        add[rc[now]]=0;        maxv[lc[now]]=minv[lc[now]]=maxv[rc[now]]=minv[rc[now]]=setv[now];        sum[lc[now]]=(m-L+1)*setv[now];        sum[rc[now]]=(R-m)*setv[now];        setv[lc[now]]=setv[now];        setv[rc[now]]=setv[now];        setv[now]=-1;    }}void pushdownad(int now,int L,int R){    if(add[now]>0)    {        int m=(L+R)>>1;        sum[lc[now]]+=(m-L+1)*add[now];        sum[rc[now]]+=(R-m)*add[now];        maxv[lc[now]]+=add[now];        add[lc[now]]+=add[now];        maxv[rc[now]]+=add[now];        add[rc[now]]+=add[now];        minv[lc[now]]+=add[now];        minv[rc[now]]+=add[now];        add[now]=0;    }}void build(int &now,int L,int R){    now=++np;    if(L==R)    {        maxv[now]=minv[now]=sum[now]=a[L];        return;    }    int m=(L+R)>>1;    build(lc[now],L,m);    build(rc[now],m+1,R);    pushup(now);}void update(int now,int L,int R,int i,int j,int d){    if(L>=i && R<=j)    {        minv[now]+=d;        maxv[now]+=d;        sum[now]+=(R-L+1)*d;        add[now]+=d;        return;    }    pushdowncha(now,L,R);    pushdownad(now,L,R);    int m=(L+R)>>1;    if(j<=m) update(lc[now],L,m,i,j,d);    else if(i>m) update(rc[now],m+1,R,i,j,d);    else    {        update(lc[now],L,m,i,m,d);        update(rc[now],m+1,R,m+1,j,d);    }    pushup(now);}void change(int now,int L,int R,int i,int j,int d){    if(L>=i && R<=j)    {        sum[now]=(R-L+1)*d;        maxv[now]=minv[now]=d;        setv[now]=d;        add[now]=0;//保证了若刷值后仍有add,则必定加值在刷值之后         return;    }    pushdowncha(now,L,R);    pushdownad(now,L,R);    int m=(R+L)>>1;    if(j<=m) change(lc[now],L,m,i,j,d);    else if(i>m) change(rc[now],m+1,R,i,j,d);    else     {        change(lc[now],L,m,i,m,d);        change(rc[now],m+1,R,m+1,j,d);    }    pushup(now);}int querysum(int now,int L,int R,int i,int j){    if(L>=i && R<=j) return sum[now];    pushdowncha(now,L,R);    pushdownad(now,L,R);    int m=(L+R)>>1;    if(j<=m) return querysum(lc[now],L,m,i,j);    else if(i>m) return querysum(rc[now],m+1,R,i,j);    else    {        return querysum(lc[now],L,m,i,m)+querysum(rc[now],m+1,R,m+1,j);    }}int querymin(int now,int L,int R,int i,int j){    if(L>=i && R<=j) return minv[now];    pushdowncha(now,L,R);    pushdownad(now,L,R);    int m=(L+R)>>1;    if(j<=m) return querymin(lc[now],L,m,i,j);    else if(i>m) return querymin(rc[now],m+1,R,i,j);    else    {        return min(querymin(lc[now],L,m,i,m),querymin(rc[now],m+1,R,m+1,j));    }}int querymax(int now,int L,int R,int i,int j){    if(L>=i && R<=j) return maxv[now];    pushdowncha(now,L,R);    pushdownad(now,L,R);    int m=(L+R)>>1;    if(j<=m) return querymax(lc[now],L,m,i,j);    else if(i>m) return querymax(rc[now],m+1,R,i,j);    else    {        return max(querymax(lc[now],L,m,i,m),querymax(rc[now],m+1,R,m+1,j));    }}void dfs(int now,int L,int R){    if(now)    {        printf("%d %d %d\n",L,R,sum[now]);        int m=(L+R)>>1;        dfs(lc[now],L,m);        dfs(rc[now],m+1,R);    }}int main(){//  freopen("in.txt","r",stdin);//  freopen("out.txt","w",stdout);    scanf("%d%d",&n,&m);    initial();    for(int i=1;i<=n;i++) scanf("%d",&a[i]);    build(rt=0,1,n);    while(m--)    {        scanf("%d%d%d",&ch,&x,&y);        if(ch==1)        {            scanf("%d",&d);            update(rt,1,n,x,y,d);        }        else if(ch==2)        {            scanf("%d",&d);            change(rt,1,n,x,y,d);        }        else         {            printf("%d %d %d\n",querysum(rt,1,n,x,y),querymin(rt,1,n,x,y),querymax(rt,1,n,x,y));        }//      cout<<m<<endl;//      dfs(rt,1,n);//      cout<<endl;    }    return 0;}

讲道理这题真的坑:
1.关于lazy数组add[i]和setv[i]的判断顺序和是否清零的问题,先判断setv[i]是否>=0,然后进行下放,注意此时add[lc[now]]和add[rc[now]]要清零,add[now]不用,因为在之前change()函数里面已经排除了先加再清零的可能性,所以此处add[now]不变,若它有值则下一步继续修改值。
2.v可取到0,所以在初始化setv[i]的时候记得把值赋为-1。




上面的代码中基本包含了线段树的基本运算:

//1. 初始化:void initial(){rt=np=0;memset(rc,0,sizeof(rc));memset(lc,0,sizeof(lc));}//2. 上传操作(后序进行):void pushup(int now){    maxv[now]=max(maxv[lc[now]],maxv[rc[now]]);}//3.下传操作(先序进行):void pushdown(int now){    maxv[lc[now]]+=add[now];    add[lc[now]]+=add[now];    maxv[rc[now]]+=add[now];    add[rc[now]]+=add[now];    add[now]=0;}//4.建立(类似二叉树):void build(int &now,int L,int R){    now=++np;    if(L==R)    {        maxv[now]=a[L];        return;    }    int m=(L+R)>>1;    build(lc[now],L,m);    build(rc[now],m+1,R);    pushup(now);}//5.对区间进行操作://例如将区间i~j中的所以元素值加dvoid update(int now,int L,int R,int i,int j,int d)//把区间[i...j]的值加d {    if(L>=i && R<=j)    {        maxv[now]+=d;        add[now]+=d;        return;    }     pushdown(now);    int m=(L+R)>>1;    if(j<=m) update(lc[now],L,m,i,j,d);    else if(i>m) update(rc[now],m+1,R,i,j,d);    else     {        update(lc[now],L,m,i,m,d);        update(rc[now],m+1,R,m+1,j,d);    }    pushup(now);}//6.查找i~j中的最大元素:int query(int now,int L,int R,int i,int j){    if(L>=i && R<=j)    {        return maxv[now];    }    pushdown(now);    int m=(L+R)>>1;    if(j<=m) return query(lc[now],L,m,i,j);    else if(i>m) return query(rc[now],m+1,R,i,j);    else    {        return max(query(lc[now],L,m,i,m),query(rc[now],m+1,R,m+1,j));    }}