POJ 3468
一道线段树的区间修改题,以前写的线段树都是单点修改,这里用到了区间修改。我开始觉得单点修改和区间修改差不多,把区间内每个点进行一次单点修改就是区间修改了,这样虽是可行,但是时间复杂度却大大增加,想想看,一个点一个点地修改,这和暴力有什么区别呢。
在网上找了好多资料,终于弄明白了用lazy-tag(lazy标记)进行区间修改。我的理解是这样的,为了减小修改次数,先将要进行的修改存起来,当访问到子节点的时候,如果发现父节点带有标记,就顺便将子节点更新,就像搭顺风车一样。在这道题中,每次可能要对一个区间加上一个值,比如全集为[1,7],现在要对区间[1,4]进行操作,例如要将[1,4]每个值都加1,如果采用单点更新,那么就要访问到叶节点一个一个第更新。实际上,当我们从[1,7]访问到它的左儿子[1,4]的时候,由于此时[1,4]恰好已经覆盖了要更新的区间,于是就将[1,4].tag+=1,当下次需要访问[1,4]的子节点时,发现[1,4]带有标记,就在访问[1,4]的子节点的时候顺便将以前记下的+1操作,对[1,4]的子节点进行实施,同时将[1,4]的子节点带上标记。
#include<stdio.h> #include<string.h> #define MAX_N 100005 struct node { int left; int right; long long sum;//区间和 long long tag;//标记,用来存储操作 }segTree[4*MAX_N]; void build(int,int,int);//建树 void update(int,int,int,int);//更新 void pushdown(int);//对子节点进行"秋后算账" void pushup(int);//“算账”后,更新父节点 long long query(int,int,int); int main() { int n,q; while(scanf("%d%d",&n,&q)!=EOF) { memset(segTree,0,sizeof(segTree)); build(1,1,n); int i; for(i=1;i<=n;i++) { int num; scanf("%d",&num); update(1,i,i,num); } for(i=0;i<q;i++) { char ope[2]; scanf("%s",ope); if(ope[0]=='C') { int a,b,c; scanf("%d%d%d",&a,&b,&c); update(1,a,b,c); } else { int a,b; scanf("%d%d",&a,&b); printf("%lld\n",query(1,a,b)); } } } return 0; } void build(int root,int le,int ri) { segTree[root].left=le; segTree[root].right=ri; if(le==ri) return ; int mid=(le+ri)/2; build(root*2,le,mid); build(root*2+1,mid+1,ri); } void update(int root,int le,int ri,int val) { if(le<=segTree[root].left&&ri>=segTree[root].right) { segTree[root].sum+=(segTree[root].right-segTree[root].left+1)*val;//算区间和 segTree[root].tag+=val;//要用+=,因为可能要记多笔“帐” return ; } if(le>segTree[root].right||ri<segTree[root].left) { return ; } pushdown(root);//走到了这一步,说明要对root进行操作了,于是顺便对root的子节点进行算账了 update(2*root,le,ri,val); update(2*root+1,le,ri,val); pushup(root);//更新一下root的sum } void pushdown(int root) { if(segTree[root].tag) { segTree[2*root].tag+=segTree[root].tag; segTree[2*root].sum+=(segTree[2*root].right-segTree[2*root].left+1)*segTree[root].tag; segTree[2*root+1].tag+=segTree[root].tag; segTree[2*root+1].sum+=(segTree[2*root+1].right-segTree[2*root+1].left+1)*segTree[root].tag; segTree[root].tag=0; } } void pushup(int root) { segTree[root].sum=segTree[2*root].sum+segTree[2*root+1].sum; } long long query(int root,int le,int ri) { if(le<=segTree[root].left&&ri>=segTree[root].right) { return segTree[root].sum; } if(le>segTree[root].right||ri<segTree[root].left) { return 0; } pushdown(root); long long ans=0; ans+=query(2*root,le,ri); ans+=query(2*root+1,le,ri); return ans; }