[BZOJ4825][HNOI2017]单旋(线段树+Splay)
4825: [Hnoi2017]单旋
Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 667 Solved: 342
[Submit][Status][Discuss]Description
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 变为根。Input
第一行单独一个正整数 m。接下来 m 行,每行描述一个操作:首先是一个操作编号 c∈[1,5],即问题描述中给出的五种操作中的编号,若 c= 1,则再输入一个非负整数 key,表示新插入节点的关键码。1≤m≤10^5,1≤key≤10^9所有出现的关键码互不相同。任何一个非插入操作,一定保证树非空。在未执行任何操作之前,树为空Output
输出共 m 行,每行一个整数,第 i 行对应第 i 个输入的操作的代价。Sample Input
5
1 2
1 1
1 3
4
5Sample Output
1
2
2
2
2
HINT
Source
代码用时:2h
感觉代码难度很低啊为什么我还是花了很长时间。。
思维比较简单,只要把三种操作想清楚了就差不多了。
比较自然的想法应该是Splay,但线段树实现复杂度和常数都更优。
线段树做法网上好像只找到一个(而且没有标记永久化的代码并不是很优美常数也稍大)
这里有一个自认为比较优的做法。
(本题线段树做法的思想还是有点巧妙的,利用了题目只单旋最值的设定)
12140KB,1108ms
1 #include<set> 2 #include<cstdio> 3 #include<algorithm> 4 #include<iostream> 5 #define rep(i,l,r) for (int i=l; i<=r; i++) 6 using namespace std; 7 8 template<typename T>inline void rd(T &x){ 9 int t; char ch; 10 for (t=0; !isdigit(ch=getchar()); t=(ch=='-')); 11 for (x=ch-'0'; isdigit(ch=getchar()); x=x*10+ch-'0'); 12 if (t) x=-x; 13 } 14 15 const int N=200100; 16 int m,tot,d,rt,D[N<<2],rs[N],ls[N],fa[N],a[N],b[N],op[N][2]; 17 set<int>S; 18 set<int>::iterator it,sub,pro; 19 20 void mdf(int x,int L,int R,int l,int r,int k){ 21 if (L==l && R==r){ D[x]+=k; return; } 22 int mid=(L+R)>>1; 23 if (r<=mid) mdf(x<<1,L,mid,l,r,k); 24 else if (l>mid) mdf((x<<1)|1,mid+1,R,l,r,k); 25 else mdf(x<<1,L,mid,l,mid,k),mdf((x<<1)|1,mid+1,R,mid+1,r,k); 26 } 27 28 int que(int x,int L,int R,int k){ 29 if (L==R) return D[x]; 30 int mid=(L+R)>>1; 31 if (k<=mid) return D[x]+que(x<<1,L,mid,k); 32 else return D[x]+que((x<<1)|1,mid+1,R,k); 33 } 34 35 int main(){ 36 freopen("bzoj4825.in","r",stdin); 37 freopen("bzoj4825.out","w",stdout); 38 rd(m); 39 rep(i,1,m){ 40 rd(op[i][0]); if (op[i][0]==1) rd(op[i][1]); 41 if (op[i][0]==1) b[++tot]=op[i][1]; 42 } 43 sort(b+1,b+tot+1); tot=unique(b+1,b+tot+1)-b-1; 44 rep(i,1,m) if (op[i][0]==1) a[i]=lower_bound(b+1,b+tot+1,op[i][1])-b; 45 rep(i,1,m){ 46 if (op[i][0]==1){ 47 if (S.empty()) rt=a[i],d=1; 48 else{ 49 it=S.upper_bound(a[i]),sub=it; 50 if (sub==S.begin()) ls[*sub]=a[i],fa[a[i]]=*sub,d=que(1,1,tot,*sub)+1; 51 else{ 52 pro=--it; 53 if (sub==S.end()) rs[*pro]=a[i],fa[a[i]]=*pro,d=que(1,1,tot,*pro)+1; 54 else if (que(1,1,tot,*sub)<que(1,1,tot,*pro)) 55 rs[*pro]=a[i],fa[a[i]]=*pro,d=que(1,1,tot,*pro)+1; 56 else ls[*sub]=a[i],fa[a[i]]=*sub,d=que(1,1,tot,*sub)+1; 57 } 58 } 59 printf("%d\n",d); mdf(1,1,tot,a[i],a[i],d-que(1,1,tot,a[i])); 60 S.insert(a[i]); 61 } 62 if (op[i][0]==2 || op[i][0]==4){ 63 it=S.begin(); d=que(1,1,tot,*it); printf("%d\n",d); 64 if ((*it)!=rt){ 65 mdf(1,1,tot,1,tot,1); mdf(1,1,tot,*it,*it,-d); 66 int t=fa[*it]; ls[t]=fa[*it]=0; 67 if (rs[*it]){ 68 mdf(1,1,tot,(*it)+1,t-1,-1); 69 fa[rs[*it]]=t; ls[t]=rs[*it]; 70 } 71 rs[*it]=rt; fa[rt]=*it; rt=*it; 72 } 73 if (op[i][0]==4) rt=rs[*it],rs[*it]=fa[rt]=0,mdf(1,1,tot,1,tot,-1),S.erase(*it); 74 } 75 if (op[i][0]==3 || op[i][0]==5){ 76 it=S.end(); it--; d=que(1,1,tot,*it); printf("%d\n",d); 77 if ((*it)!=rt){ 78 mdf(1,1,tot,1,tot,1); mdf(1,1,tot,*it,*it,-d); 79 int t=fa[*it]; rs[t]=fa[*it]=0; 80 if (ls[*it]){ 81 mdf(1,1,tot,t+1,(*it)-1,-1); 82 fa[ls[*it]]=t; rs[t]=ls[*it]; 83 } 84 ls[*it]=rt; fa[rt]=*it; rt=*it; 85 } 86 if (op[i][0]==5) rt=ls[*it],ls[*it]=fa[rt]=0,mdf(1,1,tot,1,tot,-1),S.erase(*it); 87 } 88 } 89 return 0; 90 }
惊了,splay跑的比线段树还快。
5752KB,900ms
代码用时:1h
一开始想自己写出来,思路并不复杂但代码很繁琐(模板占了大部分但想敲对并不容易)。
看了网上的代码抄了一份下来竟然1A,可能是我抄代码的经历中最顺利的一次吧。
但抄来的代码终究不是自己的,要勇于写这种代码稍长的题目,后面还会有比这长的多的题!
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #define ls c[x][0] 5 #define rs c[x][1] 6 #define rep(i,l,r) for (int i=l; i<=r; i++) 7 using namespace std; 8 9 template<typename T>inline void rd(T &x){ 10 int t; char ch; 11 for (t=0; !isdigit(ch=getchar()); t=(ch=='-')); 12 for (x=ch-'0'; isdigit(ch=getchar()); x=x*10+ch-'0'); 13 if (t) x=-x; 14 } 15 16 const int inf=2000000000,N=100100; 17 int n,cnt,rt,Q,fa[N],v[N],sz[N],c[N][2],s[N],dep[N],mn[N]; 18 19 void push(int x){ 20 v[ls]+=v[x]; dep[ls]+=v[x]; mn[ls]+=v[x]; 21 v[rs]+=v[x]; dep[rs]+=v[x]; mn[rs]+=v[x]; 22 v[x]=0; 23 } 24 25 void upd(int x){ 26 sz[x]=sz[ls]+sz[rs]+1; mn[x]=dep[x]; 27 if (ls) mn[x]=min(mn[x],mn[ls]); 28 if (rs) mn[x]=min(mn[x],mn[rs]); 29 } 30 31 void rot(int &rt,int x){ 32 int y=fa[x],z=fa[y],w=(c[y][1]==x); 33 if (y==rt) rt=x; else c[z][c[z][1]==y]=x; 34 fa[x]=z; fa[y]=x; fa[c[x][w^1]]=y; 35 c[y][w]=c[x][w^1]; c[x][w^1]=y; upd(y); 36 } 37 38 void splay(int &rt,int x){ 39 while (x!=rt) { 40 int y=fa[x],z=fa[y]; 41 if (y!=rt){ 42 if ((c[z][1]==y)^(c[y][1]==x)) rot(rt,x); else rot(rt,y); 43 } 44 rot(rt,x); 45 } 46 upd(x); 47 } 48 49 void ins(int &x,int S,int d,int lst){ 50 if (!x){ 51 x=++cnt, s[cnt]=S; dep[cnt]=mn[cnt]=d; 52 sz[cnt]=1; fa[cnt]=lst; return; 53 } 54 ins(c[x][S>s[x]],S,d,x); upd(x); 55 } 56 57 int getpre(int x,int S){ 58 if (!x) return 0; 59 if (v[x]) push(x); 60 if (s[x]>S) return getpre(c[x][0],S); 61 Q=getpre(c[x][1],S); 62 if (Q) return Q; else return x; 63 } 64 65 int getnxt(int x,int S){ 66 if (!x) return 0; 67 if (v[x]) push(x); 68 if (s[x]<S) return getnxt(rs,S); 69 Q=getnxt(ls,S); 70 if (Q) return Q; else return x; 71 } 72 73 int find(int x,int k){ 74 if (v[x]) push(x); 75 if (sz[ls]+1==k) return x; 76 if (sz[ls]+1<k) return find(rs,k-sz[ls]-1); 77 return find(ls,k); 78 } 79 80 int getl(int x,int d){ 81 if (!x) return 0; 82 if (v[x]) push(x); 83 if (min(mn[ls],dep[x])>=d) return getl(rs,d)+sz[ls]+1; 84 else return getl(ls,d); 85 } 86 87 int getr(int x,int d){ 88 if (!x) return 0; 89 if (v[x]) push(x); 90 if (min(mn[rs],dep[x])>=d) return getr(ls,d)+sz[rs]+1; 91 else return getr(rs,d); 92 } 93 94 int split(int l,int r){ 95 int t1=find(rt,l-1),t2=find(rt,r+1); 96 splay(rt,t1); splay(c[rt][1],t2); return c[c[rt][1]][0]; 97 } 98 99 void mdf(int l,int r,int ad){ 100 int y=split(l,r); v[y]+=ad; mn[y]+=ad; dep[y]+=ad; 101 } 102 103 void change(int x,int S){ 104 if (v[x]) push(x); 105 if (s[x]==S) dep[x]=1; else change(c[x][S>s[x]],S); 106 upd(x); 107 } 108 109 int main(){ 110 freopen("bzoj4825.in","r",stdin); 111 freopen("bzoj4825.out","w",stdout); 112 rd(n); ins(rt,-inf,inf,0); ins(rt,inf,inf,0); mn[0]=inf; 113 rep(i,1,n){ 114 int op,x; rd(op); 115 if (op==1){ 116 rd(x); int t1=getpre(rt,x),t2=getnxt(rt,x); 117 int D=max(t1>2 ? dep[t1] : 0,t2>2 ? dep[t2] : 0)+1; 118 ins(rt,x,D,0); splay(rt,cnt); printf("%d\n",D); 119 } 120 if (!(op&1)){ 121 int x=find(rt,2),y=min(getl(rt,dep[x]),sz[rt]-1)-1; 122 printf("%d\n",dep[x]); mdf(2,sz[rt]-1,1); 123 if (y>1) mdf(2,y+1,-1); 124 change(rt,s[x]); 125 } 126 if ((op&1)&&(op>1)){ 127 int x=find(rt,sz[rt]-1),y=min(getr(rt,dep[x]),sz[rt]-1)-1; 128 printf("%d\n",dep[x]); mdf(2,sz[rt]-1,1); 129 if (y>1) mdf(sz[rt]-y,sz[rt]-1,-1); 130 change(rt,s[x]); 131 } 132 if (op>=4){ 133 if (op==4) splay(rt,find(rt,2)); 134 else splay(rt,find(rt,sz[rt]-1)); 135 int l=(op==5),r=l^1,y=c[rt][l]; 136 c[y][r]=c[rt][r]; fa[y]=0; fa[c[rt][r]]=y; rt=y; 137 v[rt]-=1; upd(rt); 138 } 139 } 140 return 0; 141 }