splay区间操作(bzoj1500)
其实这道题思路还是满简单的,只是代码量和debug让人感到痛苦,但还是蛮锻炼能力的
还是说说各个操作
插入
不同普通题的是,插入是插入一段。如果一个一个插的话会很慢,我们可以先把要插入的一段建成一个平衡树,再一起插入。
删除
删除也是删除一段区间[L,R],我们可以把L-1旋转到根,R+1旋转到根的右儿子,那么[L,R]就在根的右儿子的左二子,删除就可以了。
注意:每次删除的点是可以进行回收再利用的(节省空间)
修改
修改一段区间打上标记就好。
翻转
同样的打标记(而且和修改的标记是不影响的),可参考文艺平衡树。
求和
其实就类似于线段树的求和,节点维护sum就好了
求和最大的子列
我们在每个节点会维护3个信息,lx最大前缀,rx最大后缀,maxx最大子列
pushup时注意一下更新的方法就好
#include<bits/stdc++.h> #define INF 2100000001 #define N 500003 #define M 4000003 #define LL long long using namespace std; int read() { int x=0,f=1;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} return x*f; } int root,ndnum=0; char s[12]; int a[N]; int ch[N][3],rev[N],tag[N],siz[N],f[N],key[N]; LL sum[N],lx[N],rx[N],maxx[N]; queue<int>q; int get(int x) { return ch[f[x]][1]==x; } void update(int x) { if(!x)return ; int l=ch[x][0],r=ch[x][1]; siz[x]=siz[l]+siz[r]+1; sum[x]=sum[l]+sum[r]+key[x]; lx[x]=max(lx[l],sum[l]+key[x]+max((LL)0,lx[r])); rx[x]=max(rx[r],sum[r]+key[x]+max((LL)0,rx[l])); maxx[x]=max( max(rx[l],(LL)0)+key[x]+max(lx[r],(LL)0) ,max(maxx[l],maxx[r])); } int build(int fa,int l,int r) { if(l>r)return 0; int mid=(l+r)>>1,now; if(q.empty()) now=++ndnum; else { now=q.front();q.pop(); } key[now]=a[mid]; f[now]=fa;tag[now]=-INF;sum[now]=0;siz[now]=1; rev[now]=0;//这些标记在新用和回收的之后一定要清空而不能有冗余 ch[now][0]=build(now,l,mid-1); ch[now][1]=build(now,mid+1,r); update(now);//printf(">>>%d %lld\n",now,maxx[now]);//printf("###%d\n",sum[now]); return now; } void modify(int k,int c) { if(!k)return; tag[k]=key[k]=c; sum[k]=siz[k]*c; maxx[k]=rx[k]=lx[k]=max(sum[k],(LL)c);//如果是改成负数,最大只选一个,不然最大应该是和 } void turn(int k) { if(!k) return; rev[k]^=1; swap(ch[k][0],ch[k][1]); swap(lx[k],rx[k]); } void pushdown(int k) { if(!k) return; if(tag[k]!=-INF) { modify(ch[k][0],tag[k]); modify(ch[k][1],tag[k]); tag[k]=-INF; } if(rev[k]) { turn(ch[k][0]); turn(ch[k][1]); rev[k]=0; } } void rotate(int x) { int old=f[x];int oldf=f[old]; int which=get(x); ch[old][which]=ch[x][which^1]; f[ch[old][which]]=old; ch[x][which^1]=old;f[old]=x; f[x]=oldf; if(oldf) ch[oldf][ch[oldf][1]==old]=x; update(old);update(x); } void splay(int x,int tar) { for(int fa;(fa=f[x])!=tar;rotate(x)) if(f[fa]!=tar) rotate((get(fa)==get(x))?fa:x); if(!tar) root=x; pushdown(root); } void qing(int &x) { if(!x)return; f[x]=0; qing(ch[x][0]);qing(ch[x][1]); lx[x]=rx[x]=maxx[x]=-INF; siz[x]=0; tag[x]=-INF;rev[x]=0;sum[x]=0; q.push(x); ch[x][0]=ch[x][1]=0; x=0; } int find(int x) { int now=root; while(1) { pushdown(now); if(x<=siz[ch[now][0]]) now=ch[now][0]; else { x-=siz[ch[now][0]]+1; if(!x) return now; now=ch[now][1]; } } } LL query_sum(int k) { return sum[k]; } int main() { // freopen("bzoj.out","w",stdout); int n=read(),m=read(); for(int i=1;i<=n;++i)a[i+1]=read(); a[1]=-INF;a[n+2]=-INF;//哨兵节点 rx[0]=lx[0]=maxx[0]=-INF; root=build(0,1,n+2); for(int i=1;i<=m;++i) { scanf("%s",s); if(s[0]=='I') { memset(a,0,sizeof(a)); int pos=read(),tot=read(); for(int j=1;j<=tot;++j) a[j]=read(); int tmp=build(-1,1,tot); int x=find(pos+1),y=find(pos+2); splay(x,0);splay(y,x); f[tmp]=y;ch[y][0]=tmp; update(y);update(x); } if(s[0]=='D') { int pos=read(),tot=read(); int x=find(pos),y=find(pos+tot+1); splay(x,0);splay(y,x); qing(ch[y][0]); update(y);update(x); } if(s[0]=='M'&&s[2]=='K') { int pos=read(),tot=read(),c=read(); int x=find(pos),y=find(pos+tot+1); splay(x,0);splay(y,x); modify(ch[y][0],c); update(y);update(x); } if(s[0]=='R') { int pos=read(),tot=read(); int x=find(pos),y=find(pos+tot+1); splay(x,0);splay(y,x); turn(ch[y][0]); update(y);update(x); } if(s[0]=='G') { int pos=read(),tot=read(); int x=find(pos),y=find(pos+tot+1); splay(x,0);splay(y,x); update(y);update(x); printf("%lld\n",sum[ch[y][0]]); } if(s[0]=='M'&&s[2]=='X') { printf("%lld\n",maxx[root]); } } }
特别想说一下哨兵节点,就是让我wa了很久的原因,因为题目要求求出maxx,为了不影响答案,我们把前面的和后面的哨兵都设成-INF
因为最后输出最大子列时输出root的maxx就好