BZOJ 1500 洛谷2042维护序列题解
这道题真是丧心病狂。。。。
应该很容易就可以看出做法,但是写代码写的.......
思路很简单,用一个平衡树维护一下所有的操作就好了,重点讲解一下代码的细节
首先如果按照常规写法的话,splay的节点要开到4000000,
直接炸飞,
由于任意时刻splay中的节点最多只有500000个,我们只需要开500000个节点就好了
然后将没有用的节点回收起来,下一次插入是插入到这些节点中
然后就是插入是将要插入的所有数字build成一颗平衡树,直接接在要插入的位置,这样会快很多
维护最大子段和与线段树维护最大子段和方法一样,维护以包含左端点且以左端点开头的最大字段和,以及以右端点结尾的最大字段和
还有自身的最大子段和,然后就可以通过左右儿子的信息快速算出当前节点的信息了
具体细节可以看代码
# include<cstdio> # include<iostream> # include<cmath> # include<algorithm> # include<cstring> # include<queue> using namespace std; const int inf = 0xfffffff; const int mn = 500005; int n,m,a[mn]; struct Splay { int son[mn][2],fa[mn],sum[mn],id[mn],siz[mn]; int v[mn],mx[mn],lx[mn],rx[mn],root,cnt; bool rev[mn],tag[mn]; //v[x]为自身的权值 //rev为翻转标记,sum为和,mx为最大子段和 queue<int> q; //记录无用的编号 void updown(int x) { int l=son[x][0],r=son[x][1]; sum[x]=sum[l]+sum[r]+v[x]; siz[x]=siz[l]+siz[r]+1; mx[x]=max(mx[l],max(mx[r],rx[l]+v[x]+lx[r])); lx[x]=max(lx[l],sum[l]+lx[r]+v[x]); rx[x]=max(rx[r],sum[r]+rx[l]+v[x]); } void pushdown(int x) { int l=son[x][0],r=son[x][1]; if(tag[x]) { tag[x]=rev[x]=0; if(l) tag[l]=1,v[l]=v[x],sum[l]=v[x]*siz[l]; if(r) tag[r]=1,v[r]=v[x],sum[r]=v[x]*siz[r]; if(v[x]>=0) { if(l) mx[l]=lx[l]=rx[l]=sum[l]; if(r) mx[r]=lx[r]=rx[r]=sum[r]; } else { if(l) mx[l]=v[x],lx[l]=rx[l]=0; if(r) mx[r]=v[x],lx[r]=rx[r]=0; } } if(rev[x]) { rev[x]=0; rev[l]^=1; rev[r]^=1; swap(lx[l],rx[l]); swap(rx[r],lx[r]); swap(son[l][0],son[l][1]); swap(son[r][0],son[r][1]); } } void rotate(int x,int &k) { int y=fa[x],z=fa[y]; int tmp; if(son[y][0]==x) tmp=1; else tmp=0; if(k==y) k=x; else { if(son[z][0]==y) son[z][0]=x; else son[z][1]=x; } son[y][tmp^1]=son[x][tmp],fa[son[y][tmp^1]]=y; son[x][tmp]=y; fa[y]=x,fa[x]=z; updown(y),updown(x); } void splay(int x,int &k) { while(x!=k) { int y=fa[x],z=fa[y]; if(y!=k) { if((son[z][0]==y) ^ (son[y][0]==x)) rotate(x,k); else rotate(y,k); } rotate(x,k); } } int find(int x,int rk) { pushdown(x); int l=son[x][0],r=son[x][1]; if(siz[l]+1==rk) return x; else if(siz[l]>=rk) return find(l,rk); else return find(r,rk-siz[l]-1); } int split(int k,int tot) { int x=find(root,k),y=find(root,k+tot+1); //printf("QWQ\n"); splay(x,root),splay(y,son[x][1]); //printf("QWQ\n"); return son[y][0]; } void recyc(int x) { int &l=son[x][0],&r=son[x][1]; if(l) recyc(l); if(r) recyc(r); q.push(x); fa[x]=l=r=tag[x]=rev[x]=0; } void update() { int k,tot,val; scanf("%d%d%d",&k,&tot,&val); int x=split(k,tot),y=fa[x]; v[x]=val; tag[x]=1; sum[x]=siz[x]*val; if(val>=0) lx[x]=rx[x]=mx[x]=sum[x]; else lx[x]=rx[x]=0,mx[x]=v[x]; updown(y); updown(fa[y]); } void rever() { int k,tot; scanf("%d%d",&k,&tot); int x=split(k,tot),y=fa[x]; if(!tag[x]) { rev[x]^=1; swap(lx[x],rx[x]); swap(son[x][0],son[x][1]); updown(y); updown(fa[y]); } } void build(int l,int r,int f) { int mid=(l+r)>>1,now=id[mid],pre=id[f]; if(l==r) { mx[now]=sum[now]=a[l]; tag[now]=rev[now]=0; lx[now]=rx[now]=max(a[l],0); siz[now]=1; } if(l<mid) build(l,mid-1,mid); if(r>mid) build(mid+1,r,mid); v[now]=a[mid]; fa[now]=pre; updown(now); son[pre][(mid>=f)]=now; } void ins() { int k,tot; scanf("%d%d",&k,&tot); for(int i=1; i<=tot; i++) scanf("%d",&a[i]); for(int i=1; i<=tot; i++) if(!q.empty()) { id[i]=q.front(); q.pop(); } else id[i]=++cnt; build(1,tot,0);//将要加入的数字建一颗splay int z=id[(1+tot)>>1];//取根 int x=find(root,k+1),y=find(root,k+2); splay(x,root),splay(y,son[x][1]); fa[z]=y; son[y][0]=z; updown(y),updown(x); } void del() { int k,tot; scanf("%d%d",&k,&tot); int x=split(k,tot); int y=fa[x]; recyc(x); son[y][0]=0; updown(y),updown(fa[y]); } void query() { int k,tot; scanf("%d%d",&k,&tot); int x=split(k,tot); //printf("sss\n"); printf("%d\n",sum[x]); } } T; int main() { int x,y; char opt[20]; scanf("%d%d",&n,&m); T.mx[0]=a[1]=a[n+2]=-inf; for(int i=1; i<=n; i++) scanf("%d",&a[i+1]); for(int i=1; i<=n+2; i++) T.id[i]=i; T.cnt=n+2; T.build(1,n+2,0); T.root=(n+3)>>1; for(int i=1; i<=m; i++) { scanf("%s",opt); if(opt[0]=='I') T.ins(); else if(opt[0]=='D') T.del(); else if(opt[0]=='M' && opt[2]=='K') T.update(); else if(opt[0]=='R') T.rever(); else if(opt[0]=='G') T.query(); else printf("%d\n",T.mx[T.root]); } return 0; }