区间翻转问题 Splay
2017-11-25 11:19
141 查看
NKOJ 2504 区间翻转问题
问题描述给你一个长度为N的序列{ai}和M个操作
1.查询第k个数的值
2.将第k个数增加d
3.查询一段区间的和
4.查询一段区间的最大值
5.将一段区间镜面翻转(例如序列{1,2,3,4,5,6},将从2到5的区间翻转后得到序列{1,5,4,3,2,6})
对于除操作2,5以外的操作,输出相应的答案
输入格式
第一行两个正整数N,M
第二行N个整数,为初始的序列
第三行到底M+2行,每行若干个整数
·如果第一个数为1,那么后面一个正整数k,表示查询第k个数的值
·如果第一个数是2,那么后面两个正整数k,d,表示将ak增加d
·如果第一个数为3,那么后面两个正整数l,r,表示查询从al到ar的区间和
·如果第一个数为4,那么后面两个正整数l,r,表示查询从al到ar的最大值
·如果第一个数为5,那么后面两个正整数l,r,表示翻转从al到ar的这个区间
输出格式
除操作2,5外每个操作输出占一行,一个整数,为本次提问的答案
样例输入
6 8
1 2 3 4 5 6
1 4
3 2 5
4 2 2
5 2 5
3 1 3
5 2 5
2 5 1
4 1 6
样例输出
4
14
2
10
6
数据规模
2<=N<=100000
1<=M<=100000
原序列1<=ai<=1000
每次1<=k<=N,1<=l<=r<=N,1<=d<=1000
来源
感谢nodgd命题并提供数据
Splay裸题,主要是留个纪念。
首先按照编号建立平衡树,具体方法是加两个虚拟节点1,N+2,再将原数列中的数按key值为编号+1插入平衡树。
对于区间操作的整体思想是:提取一个区间[l,r],就把key为(l+1)-1的节点旋转到根,再把key为(r+1)+1的节点旋转到根的右儿子。此时这个节点的左子树就正好是需要提取的区间。这个由二叉检索树的性质是正确的。
于是,要得到区间和、区间极值,只需要维护以某个节点为根的子树的和、子树中的权值最大值即可。这些容易在旋转的同时更新。
修改某个点的权值,我采用的是先将该点旋转到根,再更新根节点的Max,Sum,val值。也可以在查找该点的同时更新路径上所有点的Max,Sum值。
最后是区间翻转问题。提取区间后,只需要打上lazy标记,访问到的时候下放,交换左右两棵子树即可。虽然Splay是自下往上的过程,但是Splay前一定要先找到需要Splay到根的点。这个过程是自上往下的,此时下放即可,不会造成操作的冲突。
注意lazy与线段树里略有不同:线段树中打上lazy标记的点一定是已经处理完该点的操作了,而本题中是下放时才进行操作。所以打lazy标记时应当用lazy^=1而不是lazy=1。
代码:
#include<stdio.h> #include<algorithm> #define MAXN 100005 using namespace std; int Tmax(int x,int y,int z) { if(x>=y&&x>=z)return x; if(y>=z)return y; return z; } int N; int rt,tot,fa[MAXN],ls[MAXN],rs[MAXN],pri[MAXN],val[MAXN],lazy[MAXN],Sum[MAXN],Max[MAXN],Size[MAXN]; void Update(int x,int y) { Size[y]=Size[ls[y]]+Size[rs[y]]+1; Size[x]=Size[ls[x]]+Size[rs[x]]+1; Sum[y]=Sum[ls[y]]+Sum[rs[y]]+val[y]; Sum[x]=Sum[ls[x]]+Sum[rs[x]]+val[x]; Max[y]=Tmax(Max[ls[y]],Max[rs[y]],val[y]); Max[x]=Tmax(Max[ls[x]],Max[rs[x]],val[x]); } void Putdown(int p) { lazy[ls[p]]^=1;lazy[rs[p]]^=1;lazy[p]=0; swap(ls[p],rs[p]); } void Zig(int x) { int y=fa[x],z=fa[y]; if(z) { if(ls[z]==y)ls[z]=x; else rs[z]=x; } fa[x]=z;fa[y]=x;fa[rs[x]]=y; ls[y]=rs[x];rs[x]=y; Update(x,y); } void Zag(int x) { int y=fa[x],z=fa[y]; if(z) { if(ls[z]==y)ls[z]=x; else rs[z]=x; } fa[x]=z;fa[y]=x;fa[ls[x]]=y; rs[y]=ls[x];ls[x]=y; Update(x,y); } void Splay(int x,int t) { int y,z; while(fa[x]!=t) { y=fa[x];z=fa[y]; if(z==t) { if(ls[y]==x)Zig(x); else Zag(x); } else { if(ls[z]==y) { if(ls[y]==x)Zig(y),Zig(x); else Zag(x),Zig(x); } else { if(rs[y]==x)Zag(y),Zag(x); else Zig(x),Zag(x); } } } if(!t)rt=x; } void Ins(int k,int v) { tot++; int p=rt; while(p) { Size[p]++; if(Max[p]<v)Max[p]=v;Sum[p]+=v; if(k<pri[p]) { if(!ls[p]){ls[p]=tot;break;} p=ls[p]; } else { if(!rs[p]){rs[p]=tot;break;} p=rs[p]; } } Size[tot]=1;fa[tot]=p;pri[tot]=k;val[tot]=Max[tot]=Sum[tot]=v; Splay(tot,0); } int GetKth(int k) { int p=rt; while(p) { if(lazy[p])Putdown(p); if(Size[ls[p]]+1==k)return p; if(Size[ls[p]]>=k)p=ls[p]; else k-=Size[ls[p]]+1,p=rs[p]; } } void Add(int k,int d) { int p=GetKth(k+1); Splay(p,0); val[p]+=d; Max[p]=Tmax(val[p],Max[ls[p]],Max[rs[p]]); Sum[p]+=d; } void Prepare(int x,int y) { int p,q; p=GetKth(x);Splay(p,0); q=GetKth(y+2);Splay(q,rt); } int GetSum(int x,int y) { Prepare(x,y); return Sum[ls[rs[rt]]]; } int GetMax(int x,int y) { Prepare(x,y); return Max[ls[rs[rt]]]; } void Rev(int x,int y) { Prepare(x,y); int p=ls[rs[rt]]; lazy[p]^=1; } int main() { int M,i,op,x,y,p; scanf("%d%d",&N,&M); Ins(1,0);Ins(N+2,0); for(i=1;i<=N;i++) { scanf("%d",&x); Ins(i+1,x); } while(M--) { scanf("%d",&op); if(op==1) { scanf("%d",&x); p=GetKth(x+1);Splay(p,0); printf("%d\n",val[p]); } else if(op==2) { scanf("%d%d",&x,&y); Add(x,y); } else if(op==3) { scanf("%d%d",&x,&y); printf("%d\n",GetSum(x,y)); } else if(op==4) { scanf("%d%d",&x,&y); printf("%d\n",GetMax(x,y)); } else { scanf("%d%d",&x,&y); Rev(x,y); } } }
相关文章推荐
- 【例题】【Splay】NKOJ2504 区间翻转问题
- Splay解决区间问题[区间切割,区间翻转]
- Splay之区间翻转问题
- UVA 11922 Splay区间翻转+分裂+合并
- Uva 3961 Robotic Sort(Splay区间翻转)
- Splay区间翻转
- UVA 11996 Splay + LCP + Hash + 区间翻转 插入 删除
- bzoj3223[Tyvj 1729] 文艺平衡树(splay模板题:区间翻转)
- Splay树(区间添加删除 | 区间翻转)——HDU 3487 Play with Chain
- hdu 1890 Robotic Sort (Splay区间翻转)
- hdu 1890 splay区间翻转
- HDU3487(splay区间翻转+区间切割)
- sgu187:Twist and whirl -- want to cheat(splay+区间翻转)
- 平衡树 splay区间翻转 模板
- HDU 1890 Robotic Sort [平衡树splay 区间翻转]
- HDU 1890 Robotic Sort(Splay 区间翻转)
- 【洛谷 P3391】【模板】文艺平衡树 --- 区间翻转(splay)
- splay区间翻转及删除区间,平移区间
- Robotic Sort HDU - 1890 splay区间翻转
- bzoj 1552: [Cerc2007]robotic sort && bzoj 3506: [Cqoi2014]排序机械臂(splay区间翻转)