洛谷.2042.[NOI2005]维护数列(Splay)
2018-02-08 16:00
393 查看
题目链接
2017.12.24 第一次写:
时间: 2316ms (1268ms)
空间: 19.42MB (19.5MB)(O2)
注:洛谷测的时间浮动比较大
[b]2018.2.26 第二次写:(简化许多)(两份实际行数倒差不多)
时间: 2008ms (1536ms)
空间:19.51MB (19.49MB)
2017.12.24 第一次写:
时间: 2316ms (1268ms)
空间: 19.42MB (19.5MB)(O2)
注:洛谷测的时间浮动比较大
/* 插入一段数:将这些数先单独建一棵平衡树t'.将pos旋到根,pos+1旋到son[root][1],直接将t'放到son[pos+1][0]即可 删除一段数:将要删除区间旋转到r+1的左儿子上,直接删除 统一修改一段数、区间反转:同样,提取修改区间,打标记 区间求和:提取,输出记录的sum 最大子序列:有一种求最大子序列的nlogn分治算法,用于带修改的最大子序列 就是记录lmax,rmax,maxn,sum(是否包含区间左/右端点),dp思想更新 1.最多会出现4*10^6个数,任意时刻只会有5*10^5个数,所以要用一个栈或队列存储可用下标(内存池) 要id[]记录 有Delete掉的下标就存;栈里没有就++cnt Build时一定要分清是位置m还是下标rt 2.sum[ls],sum[rs]都是不包括val[rt]的,转移时注意加上 3.下传rt节点时是去用标记更新ls,rs,而不是还更新rt。没理解透。 更新rt的标记时要更新rt各值!包括rev。在下传时给子节点标记同时更新子节点 也许不这样会导致标记下传不完全,因为这题是多询问的,而文艺平衡树是在最后输出,输出时一定会更新标记。 4.区间反转会导致lmx,rmx反了,要换! 5.Down()一般只需要在Find向下递归时进行就可以 6.由于每次Split都会导致父子关系改变,所以每次更新都要Update(son[root]),Update(root) 7.初始化mx[0],这样更新最值可以不用管是否存在子节点 8.mx和lmx,rmx更新方式是不一样的! 注意转移 */ #include<cstdio> #include<cctype> #include<algorithm> #define gc() getchar() const int N=5e5+5,INF=1e9; int n,root,A ,id ,sk ,tp,cnt; int fa ,son [2],sz ,val ,lmx ,rmx ,mx ,sum ; bool tag ,rev ;//直接用val[rt]的值代替int tag[rt] inline int read() { int now=0,f=1;register char c=gc(); for(;!isdigit(c);c=gc()) if(c=='-') f=-1; for(;isdigit(c);now=now*10+c-'0',c=gc()); return now*f; } #define ls son[rt][0] #define rs son[rt][1] inline void Update(int rt) { sz[rt]=sz[ls]+sz[rs]+1; mx[rt]=std::max(mx[ls],std::max(mx[rs],rmx[ls]+val[rt]+lmx[rs])); lmx[rt]=std::max(lmx[ls],sum[ls]+val[rt]+lmx[rs]); rmx[rt]=std::max(rmx[rs],sum[rs]+val[rt]+rmx[ls]); sum[rt]=sum[ls]+val[rt]+sum[rs]; } inline void Down(int rt) { if(tag[rt]) { if(ls) { tag[ls]=1, val[ls]=val[rt], sum[ls]=sz[ls]*val[rt]; if(val[rt]>=0) lmx[ls]=rmx[ls]=mx[ls]=sum[ls]; else lmx[ls]=rmx[ls]=0,mx[ls]=val[rt]; } if(rs) { tag[rs]=1, val[rs]=val[rt], sum[rs]=sz[rs]*val[rt]; if(val[rt]>=0) lmx[rs]=rmx[rs]=mx[rs]=sum[rs]; else lmx[rs]=rmx[rs]=0,mx[rs]=val[rt]; } rev[rt]=tag[rt]=0;//有修改标记就不需要反转 } else if(rev[rt]) { rev[rt]=0, rev[ls]^=1, rev[rs]^=1; std::swap(lmx[ls],rmx[ls]), std::swap(son[ls][0],son[ls][1]); std::swap(lmx[rs],rmx[rs]), std::swap(son[rs][0],son[rs][1]); // WA: std::swap(lmx[rt],rmx[rt]); // std::swap(ls,rs); } } void Rotate(int x,int &k) { int a=fa[x],b=fa[a];bool l=son[a][1]==x,r=l^1; if(a==k) k=x; else son[son[b][1]==a]=x; fa[x]=b, fa[a]=x, fa[son[x][r]]=a; son[a][l]=son[x][r], son[x][r]=a; Update(a),Update(x); } void Splay(int x,int &k) { while(x!=k) { int a=fa[x],b=fa[a]; if(a!=k) (son[a][1]==x^son[b][1]==a)?Rotate(x,k):Rotate(a,k); Rotate(x,k); } } int Rank(int rt,int k) { while(1) { Down(rt); if(sz[ls]+1==k) return rt; if(sz[ls]>=k) rt=ls; else k-=sz[ls]+1,rt=rs; } } void Build(int l,int r,int f) { if(l>r) return; int m=l+r>>1,rt=id[m],pre=id[f]; fa[rt]=pre, son[pre][m>f]=rt;/*,sz[rt]=1;*///son[pre][m>f]:注意数组下标和位置没有关系 rev[rt]=tag[rt]=0, val[rt]=A[m];//因为sum,lmx等还会在Update重新更新 if(l==r)//所以只在这时更新这些值 { mx[rt]=sum[rt]=A[l], sz[rt]=1;//mx[rt]是这个区间(点)必须要选,所以就是A[l] lmx[rt]=rmx[rt]=std::max(val[rt],0); return; } Build(l,m-1,m), Build(m+1,r,m); Update(rt); } void Free(int rt)//时间换空间,回收无用编号 { if(!rt) return; sk[++tp]=rt; Free(ls), Free(rs); fa[rt]=ls=rs=0; // rev[rt]=tag[rt]=0; } int Split(int pos,int tot) { int x=Rank(root,pos),y=Rank(root,pos+tot+1); Splay(x,root), Splay(y,son[x][1]); return son[y][0]; } void Ins() { int pos=read()+1,tot=read(); for(int i=1;i<=tot;++i) A[i]=read(); for(int i=1;i<=tot;++i) if(tp) id[i]=sk[tp--]; else id[i]=++cnt; Build(1,tot,0); int z=id[1+tot>>1],x=Rank(root,pos),y=Rank(root,pos+1);//z:the root of t' Splay(x,root), Splay(y,son[x][1]); fa[z]=y, son[y][0]=z; Update(y), Update(x); } void Del() { int pos=read(),tot=read(),y=Split(pos,tot),x=fa[y]; Free(y), son[x][0]=0; Update(x), Update(fa[x]);//不要再用y! } void Modify() { int pos=read(),tot=read(),v=read(),rt=Split(pos,tot); tag[rt]=1,val[rt]=v,sum[rt]=sz[rt]*v; if(v>=0) lmx[rt]=rmx[rt]=sum[rt], mx[rt]=sum[rt];//lmx,rmx全选或不选 else lmx[rt]=rmx[rt]=0, mx[rt]=v;//mx全选或选一个 Update(fa[rt]), Update(root); } void Rev() { int pos=read(),tot=read(),rt=Split(pos,tot); if(!tag[rt]) { rev[rt]^=1; std::swap(ls,rs), std::swap(lmx[rt],rmx[rt]); Update(fa[rt]), Update(root); } //WA:rev[rt]^=1; // Update(fa[rt]), Update(root); } void Query() { int pos=read(),tot=read(),rt=Split(pos,tot); printf("%d\n",sum[rt]); } //void Print(int x) //{ // if(!x) return; // Down(x); // Print(son[x][0]), printf("%d ",val[x]), Print(son[x][1]); //} int main() { //#ifndef ONLINE_JUDGE // freopen("2042.in","r",stdin); //#endif // freopen("seq2005.in","r",stdin); // freopen("seq2005.out","w",stdout); n=read();int q=read(); mx[0]=A[1]=A[n+2]=-INF; for(int i=1;i<=n;++i) A[i+1]=read(),id[i]=i; id[n+1]=n+1,id[n+2]=n+2; Build(1,n+2,0), root=n+3>>1, cnt=n+2; char s[12]; while(q--) { scanf("%s",s); if(s[0]=='I') Ins(); else if(s[0]=='D') Del(); else if(s[2]=='K') Modify(); else if(s[0]=='R') Rev(); else if(s[0]=='G') Query(); else printf("%d\n",mx[root]); } return 0; }
[b]2018.2.26 第二次写:(简化许多)(两份实际行数倒差不多)
时间: 2008ms (1536ms)
空间:19.51MB (19.49MB)
/* 数列位置直接拿sz解决 需要存个id,下标并不统一 */ #include <cstdio> #include <cctype> #include <algorithm> #define gc() getchar() const int N=5e5+15,INF=1e8,MAXIN=1e6; int n,m,root; inline int read() { int now=0,f=1;register char c=gc(); for(;!isdigit(c);c=gc()) if(c=='-') f=-1; for(;isdigit(c);now=now*10+c-'0',c=gc()); return now*f; } struct SPLAY { int A ,size,fa ,son [2],val ,sz ,id ,sum ,lmax ,rmax ,smax ; int pool ,top,cnt; bool cover ,rev ; inline int New_Node(){ if(top) return pool[--top]; return ++cnt; } inline void Update(int rt) { int ls=son[rt][0],rs=son[rt][1]; sz[rt]=sz[ls]+sz[rs]+1; sum[rt]=sum[ls]+val[rt]+sum[rs]; smax[rt]=std::max(rmax[ls]+val[rt]+lmax[rs],std::max(smax[ls],smax[rs])); lmax[rt]=std::max(lmax[ls],sum[ls]+val[rt]+lmax[rs]); rmax[rt]=std::max(rmax[rs],sum[rs]+val[rt]+rmax[ls]); } inline void Set(int rt,int v) { sum[rt]=v*sz[rt], val[rt]=v, cover[rt]=1; if(v>0) smax[rt]=lmax[rt]=rmax[rt]=sum[rt]; else smax[rt]=v, lmax[rt]=rmax[rt]=0;//至少存在一元素 但是作为区间端点还是可以不要的 } inline void Rev(int rt) { std::swap(lmax[rt],rmax[rt]), std::swap(son[rt][0],son[rt][1]); rev[rt]^=1; } void PushDown(int rt) { if(cover[rt]) Set(son[rt][0],val[rt]), Set(son[rt][1],val[rt]), cover[rt]=rev[rt]=0;//这个rev没有必要了 if(rev[rt]) Rev(son[rt][0]), Rev(son[rt][1]), rev[rt]^=1; } void Rotate(int x,int &k) { int a=fa[x],b=fa[a]; bool l=son[a][1]==x,r=l^1; if(k==a) k=x; else son[b][son[b][1]==a]=x; fa[a]=x, fa[x]=b, fa[son[x][r]]=a, son[a][l]=son[x][r], son[x][r]=a; Update(a), Update(x); } void Splay(int x,int &k) { while(x!=k) { int a=fa[x],b=fa[a]; // PushDown(b), PushDown(a), PushDown(x); if(a!=k) son[a][1]==x^son[b][1]==a?Rotate(x,k):Rotate(a,k); Rotate(x,k); } } int Rank(int k,int p) { while(1) { PushDown(k); if(sz[son[k][0]]+1==p) return k; if(sz[son[k][0]]>=p) k=son[k][0]; else p-=sz[son[k][0]]+1, k=son[k][1]; } } void Build(int l,int r,int f) { if(l>r) return; int m=l+r>>1,rt=id[m],pre=id[f]; val[rt]=A[m], fa[rt]=pre, son[pre][m>f]=rt;//son[pre][val[rt]>val[pre]]=rt;同样的错误--在树中的排名是下标! cover[rt]=rev[rt]=0; if(l==r){ sz[rt]=1, sum[rt]=smax[rt]=val[rt]; if(val[rt]>0) lmax[rt]=rmax[rt]=val[rt]; else lmax[rt]=rmax[rt]=0; } else{//其余信息在Update时更新 Build(l,m-1,m), Build(m+1,r,m), Update(rt); } } void Insert() { int p=read()+1,tot=read(); for(int i=1; i<=tot; ++i) A[i]=read(),id[i]=New_Node(); Build(1,tot,0); int r=id[1+tot>>1],x=Rank(root,p),y=Rank(root,p+1); Splay(x,root), Splay(y,son[x][1]); fa[r]=y, son[y][0]=r; Update(y), Update(x); } void Free(int x) { pool[top++]=x; if(son[x][0]) Free(son[x][0]); if(son[x][1]) Free(son[x][1]); son[x][0]=son[x][1]=0;//son不会在Build时更新 // fa[x]=rev[x]=cover[x]=0; } int Split() { int p=read(),tot=read(); int x=Rank(root,p),y=Rank(root,p+tot+1); Splay(x,root), Splay(y,son[x][1]); // printf("p:%d tot:%d x:%d y:%d z:%d\n",p,tot,x,y,son[y][0]); return son[y][0]; } void Delete() { int z=Split(),y=fa[z]; /*if(z)*/ Free(z);//当前z这里不需要特判 不会删空元素 son[y][0]=0, Update(y), Update(fa[y]); } void Modify(){ int z=Split(),v=read(); Set(z,v), Update(fa[z]), Update(root);//更新! } void Reverse(){ int rt=Split(); if(!cover[rt])//在无cover标记时反转 Rev(rt), Update(fa[rt]), Update(root); } int Query_Sum(){ printf("%d\n",sum[Split()]); } // void Print(int x) // { // PushDown(x); // if(son[x][0]) Print(son[x][0]); // printf("%d:%d\n",x,val[x]); // if(son[x][1]) Print(son[x][1]); // } // void Debug() // { // puts("Debug"); // Print(root); // putchar('\n'); // } }t; int main() { n=read(),m=read(); t.id[1]=1, t.id[n+2]=n+2;//不能用0 t.A[1]=t.A[n+2]=-INF;//边界两个点要处理不能算答案! for(int i=2; i<=n+1; ++i) t.A[i]=read(),t.id[i]=i; t.cnt=n+2, t.Build(1,n+2,0); root=t.id[n+3>>1]; char opt[15]; while(m--) { scanf("%s",opt); if(opt[0]=='I') t.Insert(); else if(opt[0]=='D') t.Delete(); else if(opt[4]=='-') t.Modify(); else if(opt[0]=='R') t.Reverse(); else if(opt[0]=='G') t.Query_Sum(); else printf("%d\n",t.smax[root]); } return 0; }
相关文章推荐
- 维护数列 洛谷2042 bzoj1500 NOI2005
- NOI2005维护数列 伸展树splay
- splay BZOJ1500 NOI2005 维护数列
- 【SPLAY】NOI2005 维护数列
- noi2005维护数列 splay
- 【NOI2005】维护数列 splay
- [NOI 2005] 维护数列 splay
- bsoj 2246 【NOI2005】维护数列(splay解法)
- hdu4699 Editor 2013 多校训练第十场 D题 数列维护 splay | 线段树 | 栈!!!!!
- [NOI2005]维护数列
- [NOI2005]维护数列
- BZOJ1500: [NOI2005]维修数列 Splay维护序列
- 数列维护 NOI2005
- [NOI2005]维护数列
- 【jzoj2413】【NOI2005】【维护数列】【splay】
- [NOI2005]维护数列
- 维修数列 Splay(这可能是我写过最麻烦的题之一了。。。用平衡树维护dp。。。丧心病狂啊。。。。)
- 【noi2005】维护数列
- 【NOI2005】【splay】维护数列
- NOI2005 维护数列