线段树
2015-12-02 20:36
387 查看
线段树,是可以进行如下操作的树状数据结构。
1.对区间[l,r]中所有的数字+x/修改为x;
2.查询区间[l,r]的总和/最大值/最小值……
线段树的具体思想可以参考百度百科。
以下是几道线段树的例题。
codevs 1082(线段树练习3)
线段树裸题,直接套用模板即可,代码如下。
HDU 1698
简单的以dota中屠夫为背景的题目。题意如下,给定1~n的初始均为1的数列,每次将l~r区间里的数字全部修改为一个给定值,求最终的区间总和。
利用线段树可以简单的求解,注意在区间修改时lazy标记的-1的问题。
bzoj1012
具体题意请阅读链接中的题意,这道题对于审题能力有很高的要求。
简单题,运用线段树十分容易理解,也可以使用单调队列或单调栈求解,以下给出使用线段树求解的代码。
poj 2828
一道以春运为背景的题目,一共有n次插入,每次插入时将编号为val的人插在第pos个人身后,求最终val的序列。
从后往前枚举,只需插入至从第一个开始的第pos+1个空位即可,代码如下。
SGU 128
一道较难的题,具体题意请自主翻译。
解决时需要用到并查集,注意:最终所有的点必须全部联通。
代码如下:
鉴于并查集写的太过形象生动,请读者借鉴时注意。
对于想要继续学习线段树的相关知识,可以继续学习以下算法/数据结构:
树状数组(BIT)(作用类似于线段树,但只支持单点的修改)
zkw线段树(线段树的非递归写法)
主席树(可持久化线段树)
树链剖分(线段树在一棵树上的应用)
对于更多的线段树资料,可以参考《挑战程序设计竞赛(第二版)》(【日】秋叶拓哉 岩田阳一 北川宜稔 著,巫泽俊 庄俊元 李津羽 译,人民邮电出版社2013年出版)3.3节以及《算法竞赛入门经典——训练指南》(刘汝佳 著,清华大学出版社2012年出版)3.2.3和3.2.4节。
另外,codeforces上也有许多线段树的好题,以下题目可供训练参考:
251D
115E
384E
343D
1.对区间[l,r]中所有的数字+x/修改为x;
2.查询区间[l,r]的总和/最大值/最小值……
线段树的具体思想可以参考百度百科。
以下是几道线段树的例题。
codevs 1082(线段树练习3)
线段树裸题,直接套用模板即可,代码如下。
#include<iostream> #include<stdio.h> #include<queue> using namespace std; struct node { int l,r; long long sum,lazy; } tree[800050]; int n; void push_down(int id) { tree[id*2].sum+=(tree[id*2].r-tree[id*2].l+1)*tree[id].lazy; tree[id*2].lazy+=tree[id].lazy; tree[id*2+1].sum+=(tree[id*2+1].r-tree[id*2+1].l+1)*tree[id].lazy; tree[id*2+1].lazy+=tree[id].lazy; tree[id].lazy=0; } void build_tree(int id,int l,int r) { tree[id].l=l,tree[id].r=r; tree[id].lazy=0; if(l==r) { scanf("%lld",&tree[id].sum); return; } int mid=(l+r)/2; build_tree(id*2,l,mid); build_tree(id*2+1,mid+1,r); tree[id].sum=tree[2*id].sum+tree[2*id+1].sum; } void insert(int id,int l,int r,long long num) { int tl=tree[id].l,tr=tree[id].r; if(tl>=l&&tr<=r) { // cout<<id<<"we"<<endl; tree[id].sum+=num*(tr-tl+1); tree[id].lazy+=num; return; } push_down(id); int mid=(tl+tr)/2; if(mid>=l) insert(id*2,l,r,num); if(mid<r) insert(id*2+1,l,r,num); tree[id].sum=tree[id*2].sum+tree[id*2+1].sum; } long long ret=0; void query(int id,int l,int r) { int tl=tree[id].l,tr=tree[id].r; if(tl>=l&&tr<=r) { // cout<<id<<endl; ret+=tree[id].sum; return; } push_down(id); int mid=(tl+tr)/2; if(mid>=l) query(id*2,l,r); if(mid<r) query(id*2+1,l,r); tree[id].sum=tree[id*2].sum+tree[id*2+1].sum; } int main() { int n; cin>>n; build_tree(1,1,n); int q; cin>>q; while(q--) { int a; scanf("%d",&a); if(a==1) { int x,y,z; scanf("%d%d%d",&x,&y,&z); insert(1,x,y,(long long)z); } else { int x,y; scanf("%d%d",&x,&y); ret=0; query(1,x,y); cout<<ret<<endl; } } }
HDU 1698
简单的以dota中屠夫为背景的题目。题意如下,给定1~n的初始均为1的数列,每次将l~r区间里的数字全部修改为一个给定值,求最终的区间总和。
利用线段树可以简单的求解,注意在区间修改时lazy标记的-1的问题。
#include<iostream> #include<stdio.h> #include<queue> using namespace std; struct node { int l,r; long long sum,lazy; } tree[800050]; int n; void push_down(int id) { if(tree[id].lazy!=-1) { tree[id*2].sum=(tree[id*2].r-tree[id*2].l+1)*tree[id].lazy; tree[id*2].lazy=tree[id].lazy; tree[id*2+1].sum=(tree[id*2+1].r-tree[id*2+1].l+1)*tree[id].lazy; tree[id*2+1].lazy=tree[id].lazy; } tree[id].lazy=-1; } void build_tree(int id,int l,int r) { tree[id].l=l,tree[id].r=r; tree[id].lazy=0; if(l==r) { tree[id].sum=1; return; } int mid=(l+r)/2; build_tree(id*2,l,mid); build_tree(id*2+1,mid+1,r); tree[id].sum=tree[2*id].sum+tree[2*id+1].sum; } void insert(int id,int l,int r,long long num) { int tl=tree[id].l,tr=tree[id].r; if(tl>=l&&tr<=r) { tree[id].sum=num*(tr-tl+1); tree[id].lazy=num; return; } push_down(id); int mid=(tl+tr)/2; if(mid>=l) insert(id*2,l,r,num); if(mid<r) insert(id*2+1,l,r,num); tree[id].sum=tree[id*2].sum+tree[id*2+1].sum; } long long ret=0; void query(int id,int l,int r) { int tl=tree[id].l,tr=tree[id].r; if(tl>=l&&tr<=r) { // cout<<id<<endl; ret+=tree[id].sum; return; } push_down(id); int mid=(tl+tr)/2; if(mid>=l) query(id*2,l,r); if(mid<r) query(id*2+1,l,r); tree[id].sum=tree[id*2].sum+tree[id*2+1].sum; } int T; int main() { cin>>T; for(int t=1;t<=T;t++) { int n; cin>>n; build_tree(1,1,n); insert(1,1,n,1); int q; cin>>q; while(q--) { int a,b,c; scanf("%d%d%d",&a,&b,&c); insert(1,a,b,c); } ret=0; query(1,1,n); printf("Case %d: The total value of the hook is %I64d.\n",t,ret); } }
bzoj1012
具体题意请阅读链接中的题意,这道题对于审题能力有很高的要求。
简单题,运用线段树十分容易理解,也可以使用单调队列或单调栈求解,以下给出使用线段树求解的代码。
#include<iostream> #include<stdio.h> #include<queue> using namespace std; struct node { int l,r; long long maxn,lazy; } tree[800050]; int n; void push_down(int id) { if(tree[id].lazy!=-1) { tree[id*2].maxn=tree[id].lazy; tree[id*2].lazy=tree[id].lazy; tree[id*2+1].maxn=tree[id].lazy; tree[id*2+1].lazy=tree[id].lazy; } tree[id].lazy=-1; } void build_tree(int id,int l,int r) { tree[id].l=l,tree[id].r=r; tree[id].lazy=-1; tree[id].maxn=0; if(l==r) { return; } int mid=(l+r)/2; build_tree(id*2,l,mid); build_tree(id*2+1,mid+1,r); tree[id].maxn=max(tree[2*id].maxn,tree[2*id+1].maxn); } void insert(int id,int l,int r,long long num) { int tl=tree[id].l,tr=tree[id].r; if(tl>=l&&tr<=r) { // cout<<id<<"we"<<endl; tree[id].maxn=num; tree[id].lazy=num; return; } push_down(id); int mid=(tl+tr)/2; if(mid>=l) insert(id*2,l,r,num); if(mid<r) insert(id*2+1,l,r,num); tree[id].maxn=max(tree[2*id].maxn,tree[2*id+1].maxn); } long long ret=0; void query(int id,int l,int r) { int tl=tree[id].l,tr=tree[id].r; if(tl>=l&&tr<=r) { // cout<<id<<endl; ret=max(tree[id].maxn,ret); return; } push_down(id); int mid=(tl+tr)/2; if(mid>=l) query(id*2,l,r); if(mid<r) query(id*2+1,l,r); tree[id].maxn=max(tree[2*id].maxn,tree[2*id+1].maxn); } int main() { long long m,q; cin>>q>>m; int tot=1; build_tree(1,1,200000); while(q--) { char c[3]; long long num; scanf("%s%lld",&c,&num); if(c[0]=='A') { insert(1,tot,tot,(num+ret)%m); tot++; } else { ret=0; query(1,tot-num,tot-1); printf("%lld\n",ret); } } }
poj 2828
一道以春运为背景的题目,一共有n次插入,每次插入时将编号为val的人插在第pos个人身后,求最终val的序列。
从后往前枚举,只需插入至从第一个开始的第pos+1个空位即可,代码如下。
#include<iostream> #include<stdio.h> #include<queue> using namespace std; struct node { int l,r; long long sum,lazy; } tree[800050]; int n; void push_down(int id) { tree[id*2].sum+=(tree[id*2].r-tree[id*2].l+1)*tree[id].lazy; tree[id*2].lazy+=tree[id].lazy; tree[id*2+1].sum+=(tree[id*2+1].r-tree[id*2+1].l+1)*tree[id].lazy; tree[id*2+1].lazy+=tree[id].lazy; tree[id].lazy=0; } void build_tree(int id,int l,int r) { tree[id].l=l,tree[id].r=r; tree[id].lazy=0; if(l==r) { tree[id].sum=1; return; } int mid=(l+r)/2; build_tree(id*2,l,mid); build_tree(id*2+1,mid+1,r); tree[id].sum=tree[2*id].sum+tree[2*id+1].sum; } void insert(int id,int l,int r,long long num) { int tl=tree[id].l,tr=tree[id].r; if(tl>=l&&tr<=r) { // cout<<id<<"we"<<endl; tree[id].sum+=num*(tr-tl+1); tree[id].lazy+=num; return; } push_down(id); int mid=(tl+tr)/2; if(mid>=l) insert(id*2,l,r,num); if(mid<r) insert(id*2+1,l,r,num); tree[id].sum=tree[id*2].sum+tree[id*2+1].sum; } int ret=0; void query(int id,int l,int r) { int tl=tree[id].l,tr=tree[id].r; if(tl>=l&&tr<=r) { if(tree[id].sum==0) { ret=min(ret,tl); return; } if(tree[id].sum==tr-tl+1) return; } push_down(id); int mid=(tl+tr)/2; if(mid>=l) query(id*2,l,r); if(mid<r) query(id*2+1,l,r); tree[id].sum=tree[id*2].sum+tree[id*2+1].sum; } int a[200050],b[200050],ans[200050]; void push(int id,int yu,int num) { int tl=tree[id].l,tr=tree[id].r; if(tl==tr) { tree[id].sum--; ans[tl]=num; return; } int lson=id*2,rson=id*2+1; if(tree[lson].sum>=yu) push(lson,yu,num); else push(rson,yu-tree[lson].sum,num); tree[id].sum=tree[id*2].sum+tree[id*2+1].sum; } int main() { while(cin>>n) { build_tree(1,1,n); for(int i=n;i>=1;i--) { scanf("%d%d",&a[i],&b[i]); } for(int i=1;i<=n;i++) { a[i]++; push(1,a[i],b[i]); } for(int i=1;i<=n;i++) printf("%d%c",ans[i],i==n?'\n':' '); } }
SGU 128
一道较难的题,具体题意请自主翻译。
解决时需要用到并查集,注意:最终所有的点必须全部联通。
代码如下:
#include<iostream> #include<stdio.h> #include<queue> #include<utility> using namespace std; struct node { int l,r; int sum,lazy; } tree[80005]; int n; int bcj[10005]; void ji() { for(int i=1;i<=n;i++) bcj[i]=i; } int cha(int x) { if(bcj[x]==x) return x; return bcj[x]=cha(bcj[x]); } void bing(int x,int y) { bcj[cha(x)]=cha(y); } priority_queue<pair<int,int> > q[20005];int q1[20005]; void push_down(int id) { tree[id*2].sum+=(tree[id*2].r-tree[id*2].l+1)*tree[id].lazy; tree[id*2].lazy+=tree[id].lazy; tree[id*2+1].sum+=(tree[id*2+1].r-tree[id*2+1].l+1)*tree[id].lazy; tree[id*2+1].lazy+=tree[id].lazy; tree[id].lazy=0; } void build_tree(int id,int l,int r) { tree[id].l=l,tree[id].r=r; tree[id].lazy=0; if(l==r) { return; } int mid=(l+r)/2; build_tree(id*2,l,mid); build_tree(id*2+1,mid+1,r); tree[id].sum=tree[2*id].sum+tree[2*id+1].sum; } void insert(int id,int l,int r,long long num) { int tl=tree[id].l,tr=tree[id].r; if(tl>=l&&tr<=r) { // cout<<id<<"we"<<endl; tree[id].sum+=num*(tr-tl+1); tree[id].lazy+=num; return; } push_down(id); int mid=(tl+tr)/2; if(mid>=l) insert(id*2,l,r,num); if(mid<r) insert(id*2+1,l,r,num); tree[id].sum=tree[id*2].sum+tree[id*2+1].sum; } int yy[20050]; long long ret=0; void query(int id,int l,int r) { int tl=tree[id].l,tr=tree[id].r; if(tl>=l&&tr<=r) { // cout<<id<<endl; ret+=tree[id].sum; return; } push_down(id); int mid=(tl+tr)/2; if(mid>=l) query(id*2,l,r); if(mid<r) query(id*2+1,l,r); tree[id].sum=tree[id*2].sum+tree[id*2+1].sum; }int main() { cin>>n; ji(); int maxx=0,maxy=0; for(int i=1;i<=n;i++) { int x,y; cin>>x>>y; x+=10001,y+=10001; q[y].push(make_pair(x,i)); q1[x]++; maxx=max(x,maxx); maxy=max(y,maxy); } build_tree(1,1,maxx); long long ans=0; int tot=0; for(int i=1;i<=maxy;i++) { int qs=q[i].size(); if(qs) ++tot; if(qs%2==1) { cout<<0<<endl; return 0; } pair<int,int> pre; for(int j=1;j<=qs;j++) { pair<int,int> e=q[i].top(); int ef=e.first,es=e.second,pref=pre.first,pres=pre.second; q[i].pop(); if(j%2==0) { //cout<<es<<' '<<pres<<endl; bing(es,pres); ans-=ef; ret=0; int flag1=0,flag2=0; query(1,ef,ef); bing(es,pres); if(ret) { ans+=i-ret; bing(yy[ef],es); flag1=1; insert(1,ef,ef,-ret); } ret=0; query(1,pref,pref); if(ret) { ans+=i-ret; bing(yy[pref],pres); flag2=1; insert(1,pref,pref,-ret); } ret=0; query(1,ef,pref); if(ret) { cout<<0<<endl; return 0; } else { if(!flag1) insert(1,ef,ef,i); yy[ef]=es; if(!flag2) insert(1,pref,pref,i); yy[pref]=pres; } } else {ans+=ef,pre=e;} // cout<<ans<<endl; } } int tot1=0; for(int i=1;i<=maxx;i++) { if(q1[i]) ++tot1; if(q1[i]%2==1) { cout<<0<<endl; return 0; } } int flag=0; for(int i=1;i<n;i++) if(cha(i)!=cha(i+1)) flag=1; if(flag==1) cout<<0<<endl; else cout<<ans<<endl; }
鉴于并查集写的太过形象生动,请读者借鉴时注意。
对于想要继续学习线段树的相关知识,可以继续学习以下算法/数据结构:
树状数组(BIT)(作用类似于线段树,但只支持单点的修改)
zkw线段树(线段树的非递归写法)
主席树(可持久化线段树)
树链剖分(线段树在一棵树上的应用)
对于更多的线段树资料,可以参考《挑战程序设计竞赛(第二版)》(【日】秋叶拓哉 岩田阳一 北川宜稔 著,巫泽俊 庄俊元 李津羽 译,人民邮电出版社2013年出版)3.3节以及《算法竞赛入门经典——训练指南》(刘汝佳 著,清华大学出版社2012年出版)3.2.3和3.2.4节。
另外,codeforces上也有许多线段树的好题,以下题目可供训练参考:
251D
115E
384E
343D
相关文章推荐
- mysql分区表管理
- OpenGL step by step - tutorial_4 "hello shader"
- Struts2 DomainModel、ModelDriven接收参数
- (十六)写代码时的风格
- Adreno GPU详细介绍
- 60个有趣的经济学定律!
- ASP.NET AJAX(Atlas)和Anthem.NET——管中窥豹般小小比较
- 从大数据菜鸟走上大师的历程 Scala 第十一讲 extends
- codevs-1043 方格取数
- 指令汇C电子市场开发(一) ActionBar的使用
- 懒加载
- 指令汇C电子市场开发(一) ActionBar的使用
- 指令汇C电子市场开发(一) ActionBar的使用
- 非递减子序列的最大长度
- 点餐系统的设计与实现注意点与解决办法
- 第十四周 知原理真题
- CAS client客户端的配置,使用java config的方式
- 99小乘法以及1000-2000之间的闰年
- (十五)换行
- android网络编程(一)