线段树及其基本操作
Posted l1l1
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树及其基本操作相关的知识,希望对你有一定的参考价值。
处理何种问题:数组单点更新,单点查询,区间更新,区间求和,区间求最值。
性能:时间复杂度为O(logn)
原理:区间跟新的懒惰标记了解一下,其余略
实现步骤:略
备注:在线段树里,单点更新与单点累加和树状数组上的单点跟新有区别,树状数组还需与原数组求差值,线段树不用。
线段树的区间求最值差别不大,在此贴一份A过题的最值代码,用来对比找bug。
#include<iostream> #include<cstdio> #include<string.h> #include<algorithm> using namespace std; const int MaxN=200010; struct node { int l,r,w; }; node tree[MaxN*4]; int num[MaxN]; int n,ctor,x,ans,a,b,y; void built(int k,int ll,int rr) { tree[k].l=ll; tree[k].r=rr; if(tree[k].l==tree[k].r) { tree[k].w=num[ctor++]; } else { int m=(ll+rr)/2; built(k*2,ll,m); built(k*2+1,m+1,rr); tree[k].w=max(tree[k*2].w,tree[k*2+1].w); } } void change_point(int k) { if(tree[k].l==tree[k].r) { tree[k].w=y; } else { int m=(tree[k].l+tree[k].r)/2; if(x<=m) change_point(k*2); else change_point(k*2+1); tree[k].w=max(tree[k*2].w,tree[k*2+1].w); } } void ask_interval(int k) { if(a<=tree[k].l&&tree[k].r<=b) { ans=max(ans,tree[k].w); } else { int m=(tree[k].l+tree[k].r)/2; if(a<=m) ask_interval(k*2); if(b>m) ask_interval(k*2+1); } } int main() { int m; while(~scanf("%d%d",&n,&m)) { for(int i=1;i<=n;++i) scanf("%d",&num[i]); ctor=1; built(1,1,n); while(m--) { char c; getchar(); scanf("%c",&c); if(c==‘Q‘)//查询 { scanf("%d%d",&a,&b); ans=-100; ask_interval(1); printf("%d ",ans); } else if(c==‘U‘)//单点更新 { scanf("%d%d",&x,&y); change_point(1); } } } return 0; }
输入样例解释:
9//n个元素
1 2 3 4 5 6 7 8 9
1 5//单点查询为1,x为坐标,从1开始计数
2 5 0//单点修改为2,x为坐标,y为更新值,
3 1 5//区间查询为3,查询[a,b]区间内的元素和,
4 1 5 10//区间更新为4,将[a,b]区间内的元素均加y
输出样例解释:
5 //对于1的操作
10//对于3的操作输出
#include<iostream> #include<cstdio> #include<string.h> #include<algorithm> using namespace std; const int MaxN=100010; struct node { int l,r,w,f;//左边界,右边界,值,懒惰标记 }; node tree[MaxN*4]; int num[MaxN];//原序列 int n,ctor,x,ans,a,b,y;//元素个数,建树是计数变量,单点查询时位置,左区间,右区间,区间修改值 void built(int k,int ll,int rr)//建树 { tree[k].l=ll; tree[k].r=rr; if(tree[k].l==tree[k].r) { tree[k].w=num[ctor++]; } else { int m=(ll+rr)/2; built(k*2,ll,m); built(k*2+1,m+1,rr); tree[k].w=tree[k*2].w+tree[k*2+1].w; } } void down(int k)//懒惰标记 { tree[k*2].f+=tree[k].f; tree[k*2+1].f+=tree[k].f; tree[k*2].w+=tree[k].f*(tree[k*2].r-tree[k*2].l+1); tree[k*2+1].w+=tree[k].f*(tree[k*2+1].r-tree[k*2+1].l+1); tree[k].f=0; } void ask_point(int k)//单点查询 { if(tree[k].l==tree[k].r) { ans=tree[k].w; } else { if(tree[k].f) down(k); int m=(tree[k].l+tree[k].r)/2; if(x<=m) ask_point(k*2); else ask_point(k*2+1); } } void change_point(int k)//单点更新 { if(tree[k].l==tree[k].r) { tree[k].w+=y; } else { if(tree[k].f) down(k); int m=(tree[k].l+tree[k].r)/2; if(x<=m) change_point(k*2); else change_point(k*2+1); tree[k].w=tree[k*2].w+tree[k*2+1].w; } } void ask_interval(int k)//区间查询 { if(a<=tree[k].l&&tree[k].r<=b) { ans+=tree[k].w; } else { if(tree[k].f) down(k); int m=(tree[k].l+tree[k].r)/2; if(a<=m) ask_interval(k*2); if(b>m) ask_interval(k*2+1); } } void change_interval(int k)//区间更新 { if(a<=tree[k].l&&tree[k].r<=b) { tree[k].w+=(tree[k].r-tree[k].l+1)*y; tree[k].f+=y; } else { if(tree[k].f) down(k); int m=(tree[k].l+tree[k].r)/2; if(a<=m) change_interval(k*2); if(b>m) change_interval(k*2+1); tree[k].w=tree[k*2].w+tree[k*2+1].w; } } int main() { while(~scanf("%d",&n))//n 为元素个数 { for(int i=1; i<=n; ++i) scanf("%d",&num[i]); ctor=1; built(1,1,n); int m;//操作次数 scanf("%d",&m); while(m--) { int Q; scanf("%d",&Q); ans=0; if(Q==1)//单点查询 { scanf("%d",&x); ask_point(1); printf("%d ",ans); } else if(Q==2)//单点更新 { scanf("%d%d",&x,&y); change_point(1); } else if(Q==3)//区间查询 { scanf("%d%d",&a,&b); ask_interval(1); printf("%d ",ans); } else if(Q==4)//区间更新 { scanf("%d%d%d",&a,&b,&y); change_interval(1); } } } return 0; }
以上是关于线段树及其基本操作的主要内容,如果未能解决你的问题,请参考以下文章