【模板】线段树-区间修改
与上篇线段树一起食用效果更佳:神奇线段树(点击收获幸福)
区间修改
大概思路:
一开始当然是暴力!(会超时的否掉)不用暴力的话。。就用
————Lazy标记 :是一个延迟标记,先给最大的区间加上一个标记,下一次遇到时再执行,并同时下放标记。
(结合代码了解吧。。我不想画图。。。)
先建树
//建树 void build_tree(int x,int y,int p) { tree[p].a=x; tree[p].b=y; tree[p].maxx=tree[p].lazy=0;//初始化 if(x==y) { tree[p].maxx=a[x]; return; } int mid=(x+y)/2; build_tree(x,mid,p*2); build_tree(mid+1,y,p*2+1); tree[p].maxx=max(tree[p*2].maxx,tree[p*2+1].maxx);//求最大值 }
区间修改
//区间修改 void putdown(int p) { tree[p*2].maxx+=tree[p].lazy; tree[p*2].lazy+=tree[p].lazy;//将标记下放给左儿子 tree[p*2+1].maxx+=tree[p].lazy; tree[p*2+1].lazy+=tree[p].lazy;//下放给右儿子 tree[p].lazy=0;//将当前标记清零 } void changeplus(int x,int y,int p,int V) { if(!tree[p].lazy) putdown(p);//遇到标记就下放 if(x<=tree[p].a&&tree[p].b<=y) { tree[p].lazy+=V; tree[p].maxx+=V;//将当前的标记执行掉 return; } int mid=(tree[p].a+tree[p].b)/2; if(y<=tree[p*2].b) changeplus(x,mid,p*2,V); if(x>=tree[p*2+1].a) changeplus(mid+1,y,p*2+1,V); tree[p].maxx=max(tree[p*2].maxx,tree[p*2+1].maxx);//简单二分 }
void changeplus(int x,int y,int p,int V) { cout<<"当前节点"<<p<<endl; if(x<=tree[p].a&&tree[p].b<=y) { tree[p].lazy+=V; tree[p].maxx+=V*(tree[p].b-tree[p].a+1);//将当前的标记执行掉 ' cout<<"完全包含区间序号"<<p<<" "<<tree[p].maxx<<"懒标记"<<tree[p].lazy<<endl; return; } if(tree[p].lazy) { putdown(p); cout<<"下放"<<endl; //遇到标记就下放 int mid=(tree[p].a+tree[p].b)/2; if(x<=mid&&y>=tree[p*2].a )changeplus(x,y,p*2,V); if(y>mid&&x<=tree[p*2+1].b)changeplus(x,y,p*2+1,V); tree[p].maxx=tree[p*2].maxx+tree[p*2+1].maxx; cout<<"更新节点值"<<p<<" "<<tree[p].maxx<<"懒标记"<<tree[p].lazy<<endl; for(int i=1;i<=2*n-1;i++) { cout<<i<<" "<<tree[i].lazy<<" "<<endl; } }
查询区间最大值
//查询最大值 int ask(int x,int y,int p) { if(!tree[p].lazy) putdown(p);//有标记就下放 if(x>=tree[p].a&&y<=tree[p].b) return tree[p].maxx;//所求区间在当前区间内 int L,R,mid; mid=(tree[p].a+tree[p].b)/2; if(y<=tree[p*2].b)//在左边 L=ask(x,y,p*2); if(x>=tree[p*2+1].a)//在右边 R=ask(x,y,p*2+1); return max(L,R); }
看道题吧(点击收获RP++)
冥想ing。。。
AC代码:
(调了两天。。终于。。)
#include<iostream> #include<cstdio> using namespace std; struct node{ int a,b; long long int lazy,maxx; }tree[4000004]; int a[500005]; long long int ans; //建树 void build_tree(int x,int y,int p) { tree[p].a=x; tree[p].b=y; tree[p].maxx=tree[p].lazy=0;//初始化 if(x==y) { tree[p].maxx=a[x]; return; } int mid=(x+y)/2; build_tree(x,mid,p*2); build_tree(mid+1,y,p*2+1); tree[p].maxx=tree[p*2].maxx+tree[p*2+1].maxx; } //区间修改 void putdown(int p) { tree[p*2].maxx+=tree[p].lazy*(tree[p*2].b-tree[p*2].a+1); tree[p*2].lazy+=tree[p].lazy;//将标记下放给左儿子 tree[p*2+1].maxx+=tree[p].lazy*(tree[p*2+1].b-tree[p*2+1].a+1); tree[p*2+1].lazy+=tree[p].lazy;//下放给右儿子 tree[p].lazy=0;//将当前标记清零 } void changeplus(int x,int y,int p,int V) { if(x<=tree[p].a&&tree[p].b<=y)//处理不是正好在区间里的 { tree[p].lazy+=V; tree[p].maxx+=V*(tree[p].b-tree[p].a+1);//将当前的标记执行掉 return; } if(tree[p].lazy) putdown(p);//遇到标记就下放 int mid=(tree[p].a+tree[p].b)/2; if(x<=mid&&y>=tree[p*2].a )changeplus(x,y,p*2,V); if(y>mid&&x<=tree[p*2+1].b)changeplus(x,y,p*2+1,V); tree[p].maxx=tree[p*2].maxx+tree[p*2+1].maxx; } void ask(int x,int y,int p) { if(tree[p].lazy) putdown(p); if(x==tree[p].a&&y==tree[p].b) { ans=tree[p].maxx; return; } if(y<=tree[p*2].b) ask(x,y,p*2); if(x>=tree[p*2+1].a) ask(x,y,p*2+1); } int main() { int n,m; scanf("%d%d",&n,&m); int A,B,C,D; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } build_tree(1,n,1); for(int i=1;i<=m;i++) { scanf("%d",&A); if(A==1) { scanf("%d%d%d",&B,&C,&D); changeplus(B,C,1,D); } else { scanf("%d",&B); ask(B,B,1); printf("%lld\n",ans); ans=0; } } return 0; }
(RP++!!)