初识线段树
来源:互联网 发布:内存卡数据恢复软件 编辑:程序博客网 时间:2024/06/14 01:57
序列操作
【问题描述】
给出一个有n个元素的的数组:A[1],A[2],…,A[n],你的任务是设计一个数据结构,支持以下三种操作:
1 L R v:把A[L],A[L+1],…A[R]都增加v(v>=0)。
2 L R v:把A[L],A[L+1],…A[R]都修改成(v>=0)。
3 L R:计算A[L],A[L+1],…A[R]的元素和、最小值和最大值。
输入保证任意时刻序列中所有元素和不超过10^9。
【输入格式】
第一行包含 2 个正整数:n 和 m 。以下n行,每行一个整数,表示 A[i]。再以下 m 行,每行为上述三种操作之一。
【输出格式】
针对操作类型3,依次输出元素和、最小值和最大值。
【输入样例】
10 7
1 2 3 4 5 6 7 8 9 10
3 4 8
1 3 7 2
2 4 8 5
3 3 7
1 6 9 1
2 3 5 0
3 4 7
【输出样例】
30 4 8
25 5 5
12 0 6
【数据范围】
1<=m,n<=100 000。
最初始的|A[i]|<=100。
#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>#include<cmath>#include<stack>#include<queue>#include<set>#include<map>#include<vector>#include<cctype>using namespace std;const int maxn=100005;int minv[2*maxn],np,rt,maxv[2*maxn],sum[2*maxn],add[2*maxn],setv[2*maxn],a[maxn],lc[2*maxn],rc[2*maxn];int n,m,ch,x,y,d;void initial(){ rt=np=0; for(int i=1;i<=2*n;i++) setv[i]=-1; memset(lc,0,sizeof(lc)); memset(rc,0,sizeof(rc));}void pushup(int now){ minv[now]=min(minv[lc[now]],minv[rc[now]]); maxv[now]=max(maxv[lc[now]],maxv[rc[now]]); sum[now]=sum[lc[now]]+sum[rc[now]];}void pushdowncha(int now,int L,int R){ if(setv[now]>=0) { int m=(L+R)>>1; add[lc[now]]=0; add[rc[now]]=0; maxv[lc[now]]=minv[lc[now]]=maxv[rc[now]]=minv[rc[now]]=setv[now]; sum[lc[now]]=(m-L+1)*setv[now]; sum[rc[now]]=(R-m)*setv[now]; setv[lc[now]]=setv[now]; setv[rc[now]]=setv[now]; setv[now]=-1; }}void pushdownad(int now,int L,int R){ if(add[now]>0) { int m=(L+R)>>1; sum[lc[now]]+=(m-L+1)*add[now]; sum[rc[now]]+=(R-m)*add[now]; maxv[lc[now]]+=add[now]; add[lc[now]]+=add[now]; maxv[rc[now]]+=add[now]; add[rc[now]]+=add[now]; minv[lc[now]]+=add[now]; minv[rc[now]]+=add[now]; add[now]=0; }}void build(int &now,int L,int R){ now=++np; if(L==R) { maxv[now]=minv[now]=sum[now]=a[L]; return; } int m=(L+R)>>1; build(lc[now],L,m); build(rc[now],m+1,R); pushup(now);}void update(int now,int L,int R,int i,int j,int d){ if(L>=i && R<=j) { minv[now]+=d; maxv[now]+=d; sum[now]+=(R-L+1)*d; add[now]+=d; return; } pushdowncha(now,L,R); pushdownad(now,L,R); int m=(L+R)>>1; if(j<=m) update(lc[now],L,m,i,j,d); else if(i>m) update(rc[now],m+1,R,i,j,d); else { update(lc[now],L,m,i,m,d); update(rc[now],m+1,R,m+1,j,d); } pushup(now);}void change(int now,int L,int R,int i,int j,int d){ if(L>=i && R<=j) { sum[now]=(R-L+1)*d; maxv[now]=minv[now]=d; setv[now]=d; add[now]=0;//保证了若刷值后仍有add,则必定加值在刷值之后 return; } pushdowncha(now,L,R); pushdownad(now,L,R); int m=(R+L)>>1; if(j<=m) change(lc[now],L,m,i,j,d); else if(i>m) change(rc[now],m+1,R,i,j,d); else { change(lc[now],L,m,i,m,d); change(rc[now],m+1,R,m+1,j,d); } pushup(now);}int querysum(int now,int L,int R,int i,int j){ if(L>=i && R<=j) return sum[now]; pushdowncha(now,L,R); pushdownad(now,L,R); int m=(L+R)>>1; if(j<=m) return querysum(lc[now],L,m,i,j); else if(i>m) return querysum(rc[now],m+1,R,i,j); else { return querysum(lc[now],L,m,i,m)+querysum(rc[now],m+1,R,m+1,j); }}int querymin(int now,int L,int R,int i,int j){ if(L>=i && R<=j) return minv[now]; pushdowncha(now,L,R); pushdownad(now,L,R); int m=(L+R)>>1; if(j<=m) return querymin(lc[now],L,m,i,j); else if(i>m) return querymin(rc[now],m+1,R,i,j); else { return min(querymin(lc[now],L,m,i,m),querymin(rc[now],m+1,R,m+1,j)); }}int querymax(int now,int L,int R,int i,int j){ if(L>=i && R<=j) return maxv[now]; pushdowncha(now,L,R); pushdownad(now,L,R); int m=(L+R)>>1; if(j<=m) return querymax(lc[now],L,m,i,j); else if(i>m) return querymax(rc[now],m+1,R,i,j); else { return max(querymax(lc[now],L,m,i,m),querymax(rc[now],m+1,R,m+1,j)); }}void dfs(int now,int L,int R){ if(now) { printf("%d %d %d\n",L,R,sum[now]); int m=(L+R)>>1; dfs(lc[now],L,m); dfs(rc[now],m+1,R); }}int main(){// freopen("in.txt","r",stdin);// freopen("out.txt","w",stdout); scanf("%d%d",&n,&m); initial(); for(int i=1;i<=n;i++) scanf("%d",&a[i]); build(rt=0,1,n); while(m--) { scanf("%d%d%d",&ch,&x,&y); if(ch==1) { scanf("%d",&d); update(rt,1,n,x,y,d); } else if(ch==2) { scanf("%d",&d); change(rt,1,n,x,y,d); } else { printf("%d %d %d\n",querysum(rt,1,n,x,y),querymin(rt,1,n,x,y),querymax(rt,1,n,x,y)); }// cout<<m<<endl;// dfs(rt,1,n);// cout<<endl; } return 0;}
讲道理这题真的坑:
1.关于lazy数组add[i]和setv[i]的判断顺序和是否清零的问题,先判断setv[i]是否>=0,然后进行下放,注意此时add[lc[now]]和add[rc[now]]要清零,add[now]不用,因为在之前change()函数里面已经排除了先加再清零的可能性,所以此处add[now]不变,若它有值则下一步继续修改值。
2.v可取到0,所以在初始化setv[i]的时候记得把值赋为-1。
上面的代码中基本包含了线段树的基本运算:
//1. 初始化:void initial(){rt=np=0;memset(rc,0,sizeof(rc));memset(lc,0,sizeof(lc));}//2. 上传操作(后序进行):void pushup(int now){ maxv[now]=max(maxv[lc[now]],maxv[rc[now]]);}//3.下传操作(先序进行):void pushdown(int now){ maxv[lc[now]]+=add[now]; add[lc[now]]+=add[now]; maxv[rc[now]]+=add[now]; add[rc[now]]+=add[now]; add[now]=0;}//4.建立(类似二叉树):void build(int &now,int L,int R){ now=++np; if(L==R) { maxv[now]=a[L]; return; } int m=(L+R)>>1; build(lc[now],L,m); build(rc[now],m+1,R); pushup(now);}//5.对区间进行操作://例如将区间i~j中的所以元素值加dvoid update(int now,int L,int R,int i,int j,int d)//把区间[i...j]的值加d { if(L>=i && R<=j) { maxv[now]+=d; add[now]+=d; return; } pushdown(now); int m=(L+R)>>1; if(j<=m) update(lc[now],L,m,i,j,d); else if(i>m) update(rc[now],m+1,R,i,j,d); else { update(lc[now],L,m,i,m,d); update(rc[now],m+1,R,m+1,j,d); } pushup(now);}//6.查找i~j中的最大元素:int query(int now,int L,int R,int i,int j){ if(L>=i && R<=j) { return maxv[now]; } pushdown(now); int m=(L+R)>>1; if(j<=m) return query(lc[now],L,m,i,j); else if(i>m) return query(rc[now],m+1,R,i,j); else { return max(query(lc[now],L,m,i,m),query(rc[now],m+1,R,m+1,j)); }}
- 线段树初识
- 初识线段树
- 初识线段树(线段树总结)
- 敌兵布阵--初识线段树
- 线段树初识&hdu 1166 敌兵布阵
- NUIST OJ 1350-1352 面朝大海,春暖花开【初识线段树】
- 小白初识线段树(线段树原理+专题练习)
- 大战!初识线段树!SHU1979 Ticket Selling(区间更新+Lazy)
- 线段树?线段树!
- 线段树?线段树!
- 线段_线段树
- 线段_线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 个人所得税计算
- 2017.7 13 NOIP模拟赛
- bzoj2190: [SDOI2008]仪仗队
- MUI前端框架轮播图片+九宫格(左右滑动)
- BZOJ 1054 [HAOI2008]移动玩具
- 初识线段树
- linux C学习之实现简单的web服务器
- MOOC清华《程序设计基础》第5章第1题:判断数列的对称性
- shell实现简单的进度条
- python3.6 杨辉三角 小白能懂
- Android7.0多窗口模式初探
- 看透 Spring MVC 源代码分析与实践 —— 网站基础知识
- 关于servlet单选框,复选框取值问题
- Remote Access to IPython Notebooks via SSH