BZOJ1500[NOI2005]维修数列——非旋转treap
题目描述
请写一个程序,要求维护一个数列,支持以下 6 种操作:
请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格
输入
输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目。
第2行包含N个数字,描述初始时的数列。
以下M行,每行一条命令,格式参见问题描述中的表格。
任何时刻数列中最多含有500 000个数,数列中任何一个数字均在[-1 000, 1 000]内。
插入的数字总数不超过4 000 000个,输入文件大小不超过20MBytes。
输出
对于输入数据中的GET-SUM和MAX-SUM操作,向输出文件依次打印结果,每个答案(数字)占一行。
样例输入
9 8
2 -6 3 5 1 -5 -3 6 3
GET-SUM 5 4
MAX-SUM
INSERT 8 3 -5 7 2
DELETE 12 1
MAKE-SAME 3 3 2
REVERSE 3 6
GET-SUM 5 4
MAX-SUM
2 -6 3 5 1 -5 -3 6 3
GET-SUM 5 4
MAX-SUM
INSERT 8 3 -5 7 2
DELETE 12 1
MAKE-SAME 3 3 2
REVERSE 3 6
GET-SUM 5 4
MAX-SUM
样例输出
-1
10
1
10
10
1
10
提示
这道题的区间操作十分全,无论是用非旋转treap还是splay写,都会有更深入的理解。
讲一下每个操作的实现
1、将插入的数再建一棵treap,然后把原treap断裂,把新建treap合并进去。
2、把treap断裂成三部分,然后再把左右两部分合并。
3、也是把treap断裂成三部分,把中间部分打标记,标记在合并时下传。
4、和上面一样,也是断裂成三部分,中间打标记。
5、断裂成三部分,输出中间部分根节点的子树和。
6、直接输出根节点的最大连续子段和。
最后不要忘了资源回收(回收节点编号)
最后附上代码(有注释)
#include<queue> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int num; int pos; int tot;//树的大小 int root;//根节点 int n,k,g; int x,y,z; int b,c,d; char ch[5]; queue<int>q;//资源回收队列 int a[200010];//插入序列及初始序列 int m[500010];//区间最大子段和 int s[500010];//翻转标记 int f[500010];//修改标记 int r[500010];//随机数 int ls[500010];//左儿子 int rs[500010];//右儿子 int lm[500010];//子树区间中从左端点开始的最大连续子段和 int rm[500010];//子树区间中以右端点结束的最大连续子段和 int val[500010];//节点权值 int sum[500010];//子树区间和 int size[500010];//子树大小 int INF=2147483647; int insert(int v) { int x; if(!q.empty()) { x=q.front(); q.pop(); } else { x=++tot; } size[x]=1; ls[x]=rs[x]=s[x]=0; f[x]=INF; r[x]=rand(); val[x]=sum[x]=v; lm[x]=rm[x]=m[x]=v; return x; } void updata(int x) { if(!x) { return ; } size[x]=size[ls[x]]+size[rs[x]]+1; sum[x]=sum[ls[x]]+sum[rs[x]]+val[x]; m[x]=max(max(0,rm[ls[x]])+val[x]+max(0,lm[rs[x]]),max(m[ls[x]],m[rs[x]])); lm[x]=max(lm[ls[x]],sum[ls[x]]+val[x]+max(0,lm[rs[x]])); rm[x]=max(rm[rs[x]],sum[rs[x]]+val[x]+max(0,rm[ls[x]])); } void change(int x,int v) { val[x]=v; sum[x]=size[x]*v; lm[x]=rm[x]=max(0,sum[x]); m[x]=max(val[x],sum[x]); f[x]=v; } void rotate(int x) { swap(ls[x],rs[x]); swap(lm[x],rm[x]); s[x]^=1; } void downdata(int x) { if(s[x]) { if(ls[x]) { rotate(ls[x]); } if(rs[x]) { rotate(rs[x]); } } if(f[x]!=INF) { if(ls[x]) { change(ls[x],f[x]); } if(rs[x]) { change(rs[x],f[x]); } } s[x]=0; f[x]=INF; } int build(int l,int r) { if(l>r) { return 0; } int mid=(l+r)>>1; int v=a[mid]; int x=insert(v); ls[x]=build(l,mid-1); rs[x]=build(mid+1,r); updata(x); return x; } void split(int now,int t,int &x,int &y) { if(!now) { x=y=0; } else { downdata(now); if(t<=size[ls[now]]) { y=now; split(ls[now],t,x,ls[now]); } else { x=now; split(rs[now],t-size[ls[now]]-1,rs[now],y); } updata(now); } } int merge(int x,int y) { if(!x||!y) { return x+y; } downdata(x); downdata(y); if(r[x]<r[y]) { rs[x]=merge(rs[x],y); updata(x); return x; } else { ls[y]=merge(x,ls[y]); updata(y); return y; } } void inque(int x) { if(!x) { return ; } q.push(x); inque(ls[x]); inque(rs[x]); } int main() { srand(16); scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } val[0]=m[0]=-INF; root=build(1,n); while(k--) { scanf("%s",ch); if(ch[0]=='I') { scanf("%d%d",&pos,&num); for(int i=1;i<=num;i++) { scanf("%d",&a[i]); } z=build(1,num); split(root,pos,x,y); root=merge(merge(x,z),y); } else if(ch[0]=='D') { scanf("%d%d",&pos,&num); split(root,pos-1,x,y); split(y,num,b,c); root=merge(x,c); inque(b); } else if(ch[2]=='K') { scanf("%d%d%d",&pos,&num,&g); split(root,pos-1,x,y); split(y,num,b,c); change(b,g); root=merge(x,merge(b,c)); } else if(ch[0]=='R') { scanf("%d%d",&pos,&num); split(root,pos-1,x,y); split(y,num,b,c); rotate(b); root=merge(x,merge(b,c)); } else if(ch[0]=='G') { scanf("%d%d",&pos,&num); split(root,pos-1,x,y); split(y,num,b,c); printf("%d\n",sum[b]); root=merge(x,merge(b,c)); } else { printf("%d\n",m[root]); } } return 0; }