[OI学习笔记]线段树模板
背景
今天课上讲了树状数组和线段树,有是一脸懵逼。。。
于是网上有恶补了一下,
看了几个小时blog终于懂了。。。
由于很难解释,就只贴两个模板代码(一个单点修改+单点和区间查询,一个区间修改+单点和区间查询)
几点注意事项
1.首先跑区间更新的程序,一般对于每一个点要维护一个lazy变量来临时存储它将要向下更新的数值,然后对于要查寻的点,直接向下更新,这样偷懒更省时间。(具体实现过程见代码)。
2.空间要开到4*N(N为元素个数)(好像可以优化成2*N,但是本蒟蒻不知道怎么优化)
3.打线段树坑点还是很多的,下面列举一下我所犯过的错误:
1st.睿智地把mid=(tree[k].r+tree[k].l)/2和(tree[k*2].r-tree[k*2].l+1)搞混(全国可能就我会这样了。。。)
2nd.下放lazy更新子节点的权时,要用父节点的lazy更新,并且区间长度要+1.
3rd.下放完lazy父节点的lazy值要标位0!!防止重复下放!
大概就是这些,,,下面直接上代码
代码
线段树单点修改两种查询:
#include<cstdio> #include<iostream> using namespace std; const int MAXN=10010; int n;//n个元素,m个操作 struct node{ int l,r,v; }tree[4*MAXN+1];//其实最少开到4*MAXN就行 void build(int l,int r,int k){//当前节点存储的线段的左右端点&这个节点的编号 tree[k].l=l;tree[k].r=r; if(l==r){ scanf("%d",&tree[k].v);//叶子节点的值可在这里读入! return; } int mid=(l+r)/2; build(l,mid,k*2); build(mid+1,r,k*2+1); tree[k].v=tree[k*2].v+tree[k*2+1].v; } //单点查询(叶子节点) int askpoint(int x,int k){//目标点在原序列中的下标 k为当前树中的位置下标 if(tree[k].l==tree[k].r){ return tree[k].v; } int mid=(tree[k].r+tree[k].l)/2;//错点1 // if(x<=mid)return askpoint(x,k*2); else return askpoint(x,k*2+1); } //单点修改 void changepoint(int x,int num,int k){//要在序列中的第x个数中加上num k为当前树中的下标 if(tree[k].l==tree[k].r){ tree[k].v+=num; return; } int mid=(tree[k].r+tree[k].l)/2;//错点1 // if(x<=mid)changepoint(x,num,k*2); else changepoint(x,num,k*2+1); tree[k].v=tree[k*2].v+tree[k*2+1].v;//重新整合区间和 } //区间查询 int sum=0; void asksec(int tl,int tr,int l,int r,int k){ if(l>=tl&&r<=tr){ sum+=tree[k].v; return; } int mid=(tree[k].l=tree[k].r)/2; if(tl<=mid)asksec(tl,tr,l,mid,k*2); if(tr>mid)asksec(tl,tr,mid+1,r,k*2+1);//错点2 不是else// } int main(){ cout<<"点数"<<endl;//这个程序里不用问题数 scanf("%d",&n); cout<<"build tree"<<endl; build(1,n,1); cout<<"change point"<<endl; int x,num; scanf("%d%d",&x,&num); changepoint(x,num,1); cout<<"ask point"<<endl; scanf("%d",&x); printf("%d\n",askpoint(x,1)); cout<<"ask section"<<endl; int tl,tr; scanf("%d%d",&tl,&tr); asksec(tl,tr,1,n,1); printf("%d\n",sum); return 0; }
线段树区间修改两种查询:
#include<cstdio> #include<iostream> using namespace std; const int MAXN=10010; int n;//n个元素,m个操作 struct node{ int l,r,v,lazy; }tree[4*MAXN+1];//其实最少开到4*MAXN就行 void build(int l,int r,int k){//当前节点存储的线段的左右端点&这个节点的编号 tree[k].l=l;tree[k].r=r; if(l==r){ scanf("%d",&tree[k].v);//叶子节点的值可在这里读入! return; } int mid=(l+r)/2; build(l,mid,k*2); build(mid+1,r,k*2+1); tree[k].v=tree[k*2].v+tree[k*2+1].v; } //下放lazy标记函数 void putdown(int k){ tree[k*2].lazy+=tree[k].lazy; tree[k*2+1].lazy+=tree[k].lazy; tree[k*2].v+=tree[k].lazy*(tree[k*2].r-tree[k*2].l+1);//坑点:1.要用父节点k的lazy 2.区间长度记得要+1// tree[k*2+1].v+=tree[k].lazy*(tree[k*2+1].r-tree[k*2+1].l+1);//坑点:同上// tree[k].lazy=0;//坑点:父节点的lazy要清0!!!!!// } //单点查询(叶子节点) int askpoint(int x,int k){ if(tree[k].l==tree[k].r){ return tree[k].v; } if(tree[k].lazy)putdown(k);//防止重复下放 int mid=(tree[k].l+tree[k].r)/2; if(x<=mid)return askpoint(x,k*2); else return askpoint(x,k*2+1); } //区间修改 void changesection(int tl,int tr,int num,int k){ if(tl<=tree[k].l&&tr>=tree[k].r){//对查询有用 tree[k].v+=num*(tree[k].r-tree[k].l+1);//坑点// tree[k].lazy+=num; return; } if(tree[k].lazy)putdown(k);//防止重复下放 int mid=(tree[k].r+tree[k].l)/2;//错点 // if(tl<=mid)changesection(tl,tr,num,k*2); if(tr>mid)changesection(tl,tr,num,k*2+1); tree[k].v=tree[k*2].v+tree[k*2+1].v; } //区间查询 int sum=0; void asksec(int tl,int tr,int l,int r,int k){ if(l>=tl&&r<=tr){ sum+=tree[k].v; return; } if(tree[k].lazy)putdown(k);//防止重复下放 int mid=(tree[k].r+tree[k].l)/2; if(tl<=mid)asksec(tl,tr,l,mid,k*2); if(tr>mid)asksec(tl,tr,mid+1,r,k*2+1); } int main(){ freopen("in.in","r",stdin); cout<<"点数"<<endl; scanf("%d",&n); cout<<"build tree"<<endl; build(1,n,1); cout<<"change section"<<endl; int x,y,num; scanf("%d%d%d",&x,&y,&num); changesection(x,y,num,1); cout<<"ask point"<<endl; scanf("%d",&x); printf("%d\n",askpoint(x,1)); cout<<"ask section"<<endl; int tl,tr; scanf("%d%d",&tl,&tr); asksec(tl,tr,1,n,1); printf("%d\n",sum); return 0; }
本篇文章为SHINE_GEEK原创,转载请注明来源!
-------------------------------------
签名:自己选的路,跪着也要走完;理想的实现,需要不懈奋斗!
-------------------------------------
written_by:SHINE_GEEK
blog_addr:www.cnblogs.com/sjrb
-------------------------------------
签名:自己选的路,跪着也要走完;理想的实现,需要不懈奋斗!
-------------------------------------