[bzoj4825]:[Hnoi2017]单旋
来自FallDream的博客,未经允许,请勿转载,谢谢。
H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构。伸展树(splay)是一种数据结构,因为代码好写,功能多,效率高,掌握这种数据结构成为了 H 国的必修技能。有一天,邪恶的“卡”带着他的邪恶的“常数”来企图毁灭 H 国。“卡”给 H 国的人洗脑说,splay 如果写成单旋的,将会更快。“卡”称“单旋 splay”为“spaly”。虽说他说的很没道理,但还是有 H 国的人相信了,小 H 就是其中之一,spaly 马上成为他的信仰。 而 H 国的国王,自然不允许这样的风气蔓延,国王构造了一组数据,数据由 m 个操作构成,他知道这样的数据肯定打垮 spaly,但是国王还有很多很多其他的事情要做,所以统计每个操作所需要的实际代价的任务就交给你啦。
数据中的操作分为五种:
1. 插入操作:向当前非空 spaly 中插入一个关键码为 key 的新孤立节点。插入方法为,先让 key 和根比较,如果 key 比根小,则往左子树走,否则往右子树走,如此反复,直到某个时刻,key 比当前子树根 x 小,而 x 的左子树为空,那就让 key 成为 x 的左孩子; 或者 key 比当前子树根 x 大,而 x 的右子树为空,那就让 key 成为
x 的右孩子。该操作的代价为:插入后,key 的深度。特别地,若树为空,则直接让新节点成为一个单个节点的树。(各节点关键码互不相等。对于“深度”的解释见末尾对 spaly 的描述)。
2. 单旋最小值:将 spaly 中关键码最小的元素 xmin 单旋到根。操作代价为:单旋前 xmin 的深度。(对于单旋操作的解释见末尾对 spaly 的描述)。
3. 单旋最大值:将 spaly 中关键码最大的元素 xmax 单旋到根。操作代价为:单旋前 xmax 的深度。
4. 单旋删除最小值:先执行 2 号操作,然后把根删除。由于 2 号操作之后,根没有左子树,所以直接切断根和右子树的联系即可(具体见样例解释)。 操作代价同 2 号操 作。
5. 单旋删除最大值:先执行 3 号操作,然后把根删除。 操作代价同 3 号操作。
对于不是 H 国的人,你可能需要了解一些 spaly 的知识,才能完成国王的任务:
a. spaly 是一棵二叉树,满足对于任意一个节点 x,它如果有左孩子 lx,那么 lx 的关键码小于 x 的关键码。如果有右孩子 rx,那么 rx 的关键码大于 x 的关键码。
b. 一个节点在 spaly 的深度定义为:从根节点到该节点的路径上一共有多少个节点(包括自己)。
c. 单旋操作是对于一棵树上的节点 x 来说的。一开始,设 f 为 x 在树上的父亲。如果 x 为 f 的左孩子,那么执行 zig(x) 操作(如上图中,左边的树经过 zig(x) 变为了右边的树),否则执行 zag(x) 操作(在上图中,将右边的树经过 zag(f) 就变成了左边的树)。每当执 行一次 zig(x) 或者 zag(x),x 的深度减小 1,如此反复,
直到 x 为根。总之,单旋 x 就是通过反复执行 zig 和 zag 将 x 变为根
m<=10^5
很牛逼的一道题233
只有傻瓜才会写一个spaly去模拟这些操作。
因为这道题只对最值进行操作,所以每次操作全是左旋或者全是右旋
发现把最小值转上去的时候,它的右子树深度不变,其他点深度+1,然后最小值深度变为1
转最大值同理
插入的时候只可能插在前驱右儿子或者后继左儿子。实际上它们只有一个位置是空的,判断插入到哪个即可。
这个明显可以维护。只需要支持区间加,求出最长的大于某一深度的区间,插入一个点,查询前驱后继等操作即可。
因为本题可以离线,所以直接线段树就好了。
当然,既然是一道关于spaly的题,你也可以用splay直接维护哦 复杂度nlogn
#include<iostream> #include<cstdio> #define getchar() (*S++) char B[1<<26],*S=B; #define INF 2000000000 #define MN 100000 using namespace std; inline int read() { int x = 0; char ch = getchar(); while(ch < '0' || ch > '9')ch = getchar(); while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x; } int n,fa[MN+5],val[MN+5],cnt=0,c[MN+5][2],rt=0,Q,s[MN+5],dep[MN+5],mn[MN+5],size[MN+5]; inline void pushdown(int x) { int l=c[x][0],r=c[x][1]; val[l]+=val[x];dep[l]+=val[x];mn[l]+=val[x]; val[r]+=val[x];dep[r]+=val[x];mn[r]+=val[x]; val[x]=0; } inline void update(int x) { int l=c[x][0],r=c[x][1]; size[x]=size[l]+size[r]+1; mn[x]=dep[x]; if(l) mn[x]=min(mn[x],mn[l]); if(r) mn[x]=min(mn[x],mn[r]); } void rotate(int x,int&k) { int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1; if(y==k) k=x; else c[z][c[z][1]==y]=x; fa[x]=z;fa[y]=x;fa[c[x][r]]=y; c[y][l]=c[x][r];c[x][r]=y; update(y);update(x); } void splay(int x,int&k) { for(;x!=k;rotate(x,k)) if(fa[x]!=k) rotate((c[fa[fa[x]]][1]==fa[x]^c[fa[x]][1]==x)?x:fa[x],k); } void ins(int&x,int S,int d,int last) { if(!x) { x=++cnt;s[cnt]=S;dep[cnt]=mn[cnt]=d; size[cnt]=1;fa[cnt]=last;return; } ins(c[x][S>s[x]],S,d,x); update(x); } int Find_Before(int x,int S) { if(!x) return 0; if(val[x]) pushdown(x); if(s[x]>S) return Find_Before(c[x][0],S); return (Q=Find_Before(c[x][1],S))?Q:x; } int Find_After(int x,int S) { if(!x) return 0; if(val[x]) pushdown(x); if(s[x]<S) return Find_After(c[x][1],S); return (Q=Find_After(c[x][0],S))?Q:x; } int Find(int x,int rk) { if(val[x]) pushdown(x); int sz=size[c[x][0]]+1; if(sz==rk) return x; if(sz<rk) return Find(c[x][1],rk-sz); return Find(c[x][0],rk); } int FindLeft(int x,int d) { if(!x) return 0; if(val[x]) pushdown(x); if(min(mn[c[x][0]],dep[x])>=d) return FindLeft(c[x][1],d)+size[c[x][0]]+1; else return FindLeft(c[x][0],d); } int FindRight(int x,int d) { if(!x) return 0; if(val[x]) pushdown(x); if(min(mn[c[x][1]],dep[x])>=d) return FindRight(c[x][0],d)+size[c[x][1]]+1; else return FindRight(c[x][1],d); } inline int Split(int l,int r) { int Lt=Find(rt,l-1),Rt=Find(rt,r+1); splay(Lt,rt);splay(Rt,c[rt][1]); return c[c[rt][1]][0]; } inline void Modify(int l,int r,int ad) { int y=Split(l,r); val[y]+=ad;mn[y]+=ad;dep[y]+=ad; } void change(int x,int S) { if(val[x]) pushdown(x); if(s[x]==S) dep[x]=1; else change(c[x][S>s[x]],S); update(x); } int main() { fread(B,1,1<<26,stdin); n=read();ins(rt,-INF,INF,0);ins(rt,INF,INF,0);mn[0]=INF; for(int i=1;i<=n;++i) { int op=read(); if(op==1) { int x=read(),bef=Find_Before(rt,x),aft=Find_After(rt,x); int D=max(bef>2?dep[bef]:0,aft>2?dep[aft]:0)+1; ins(rt,x,D,0);splay(cnt,rt); printf("%d\n",D); } if(!(op&1)) { int x=Find(rt,2),y=min(FindLeft(rt,dep[x]),size[rt]-1)-1; printf("%d\n",dep[x]); Modify(2,size[rt]-1,1); if(y>1) Modify(2,y+1,-1); change(rt,s[x]); } if((op&1)&&op>1) { int x=Find(rt,size[rt]-1),y=min(FindRight(rt,dep[x]),size[rt]-1)-1; printf("%d\n",dep[x]); Modify(2,size[rt]-1,1); if(y>1) Modify(size[rt]-y,size[rt]-1,-1); change(rt,s[x]); } if(op>=4) { if(op==4) splay(Find(rt,2),rt); else splay(Find(rt,size[rt]-1),rt); int l=(op==5),r=l^1,y=c[rt][l]; c[y][r]=c[rt][r];fa[y]=0; fa[c[rt][r]]=y;rt=y; val[rt]-=1;update(rt); } } return 0; }
FallDream代表秋之国向您问好!
欢迎您来我的博客www.cnblogs.com/FallDream