[BZOJ4825][HNOI2017]单旋(线段树+Splay)
生活随笔
收集整理的這篇文章主要介紹了
[BZOJ4825][HNOI2017]单旋(线段树+Splay)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
4825: [Hnoi2017]單旋
Time Limit:?10 Sec??Memory Limit:?256 MBSubmit:?667??Solved:?342
[Submit][Status][Discuss]
Description
H 國是一個熱愛寫代碼的國家,那里的人們很小去學(xué)校學(xué)習(xí)寫各種各樣的數(shù)據(jù)結(jié)構(gòu)。伸展樹(splay)是一種數(shù)據(jù) 結(jié)構(gòu),因為代碼好寫,功能多,效率高,掌握這種數(shù)據(jù)結(jié)構(gòu)成為了 H 國的必修技能。有一天,邪惡的“卡”帶著 他的邪惡的“常數(shù)”來企圖毀滅 H 國。“卡”給 H 國的人洗腦說,splay 如果寫成單旋的,將會更快。“卡”稱 “單旋 splay”為“spaly”。雖說他說的很沒道理,但還是有 H 國的人相信了,小 H 就是其中之一,spaly 馬 上成為他的信仰。 而 H 國的國王,自然不允許這樣的風(fēng)氣蔓延,國王構(gòu)造了一組數(shù)據(jù),數(shù)據(jù)由 m 個操作構(gòu)成, 他知道這樣的數(shù)據(jù)肯定打垮 spaly,但是國王還有很多很多其他的事情要做,所以統(tǒng)計每個操作所需要的實際代價 的任務(wù)就交給你啦。 ? 數(shù)據(jù)中的操作分為五種: ? 1. 插入操作:向當前非空 spaly 中插入一個關(guān)鍵碼為 key 的新孤立節(jié)點。插入方法為,先讓 key 和根比較,如果? key 比根小,則往左子樹走,否則往右子樹走,如此反復(fù),直到某個時刻,key 比當前子樹根 x 小,而 x 的左子 樹為空,那就讓 key 成為 x 的左孩子; 或者 key 比當前子樹根 x 大,而 x 的右子樹為空,那就讓 key 成為? x 的右孩子。該操作的代價為:插入后,key 的深度。特別地,若樹為空,則直接讓新節(jié)點成為一個單個節(jié)點的樹 。(各節(jié)點關(guān)鍵碼互不相等。對于“深度”的解釋見末尾對 spaly 的描述)。 2. 單旋最小值:將 spaly 中關(guān)鍵碼最小的元素 xmin 單旋到根。操作代價為:單旋前 xmin 的深度。 (對于單旋操作的解釋見末尾對 spaly 的描述)。 3. 單旋最大值:將 spaly 中關(guān)鍵碼最大的元素 xmax 單旋到根。操作代價為:單旋前 xmax 的深度。 4. 單旋刪除最小值:先執(zhí)行 2 號操作,然后把根刪除。由于 2 號操作之后,根沒有左子樹,所以直接切斷根和右子 樹的聯(lián)系即可(具體見樣例解釋)。 操作代價同 2 號操 作。 5. 單旋刪除最大值:先執(zhí)行 3 號操作,然后把根刪除。 操作代價同 3 號操作。 ? 對于不是 H 國的人,你可能需要了解一些 spaly 的知識,才能完成國王的任務(wù): ? a. spaly 是一棵二叉樹,滿足對于任意一個節(jié)點 x,它如果有左孩子 lx,那么 lx 的關(guān)鍵碼小于 x 的關(guān)鍵碼。 如果有右孩子 rx,那么 rx 的關(guān)鍵碼大于 x 的關(guān)鍵碼。 b. 一個節(jié)點在 spaly 的深度定義為:從根節(jié)點到該節(jié)點的路徑上一共有多少個節(jié)點(包括自己)。 c. 單旋操作是對于一棵樹上的節(jié)點 x 來說的。一開始,設(shè) f 為 x 在樹上的父親。如果 x 為 f 的左孩子,那么 執(zhí)行 zig(x) 操作(如上圖中,左邊的樹經(jīng)過 zig(x) 變?yōu)榱擞疫叺臉?#xff09;,否則執(zhí)行 zag(x) 操作(在上圖中,將 右邊的樹經(jīng)過 zag(f) 就變成了左邊的樹)。每當執(zhí) 行一次 zig(x) 或者 zag(x),x 的深度減小 1,如此反復(fù), 直到 x 為根。總之,單旋 x 就是通過反復(fù)執(zhí)行 zig 和 zag 將 x 變?yōu)楦?/span>Input
第一行單獨一個正整數(shù) m。 接下來 m 行,每行描述一個操作:首先是一個操作編號 c∈[1,5],即問題描述中給出的五種操作中的編號,若 c ?= 1,則再輸入一個非負整數(shù) key,表示新插入節(jié)點的關(guān)鍵碼。 1≤m≤10^5,1≤key≤10^9 所有出現(xiàn)的關(guān)鍵碼互不相同。任何一個非插入操作,一定保證樹非空。在未執(zhí)行任何操作之前,樹為空Output
輸出共 m 行,每行一個整數(shù),第 i 行對應(yīng)第 i 個輸入的操作的代價。Sample Input
51 2
1 1
1 3
4
5
Sample Output
12
2
2
2
HINT
?Source
代碼用時:2h
感覺代碼難度很低啊為什么我還是花了很長時間。。
思維比較簡單,只要把三種操作想清楚了就差不多了。
比較自然的想法應(yīng)該是Splay,但線段樹實現(xiàn)復(fù)雜度和常數(shù)都更優(yōu)。
線段樹做法網(wǎng)上好像只找到一個(而且沒有標記永久化的代碼并不是很優(yōu)美常數(shù)也稍大)
這里有一個自認為比較優(yōu)的做法。
(本題線段樹做法的思想還是有點巧妙的,利用了題目只單旋最值的設(shè)定)
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
一開始想自己寫出來,思路并不復(fù)雜但代碼很繁瑣(模板占了大部分但想敲對并不容易)。
看了網(wǎng)上的代碼抄了一份下來竟然1A,可能是我抄代碼的經(jīng)歷中最順利的一次吧。
但抄來的代碼終究不是自己的,要勇于寫這種代碼稍長的題目,后面還會有比這長的多的題!
?
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 }?
轉(zhuǎn)載于:https://www.cnblogs.com/HocRiser/p/8299273.html
總結(jié)
以上是生活随笔為你收集整理的[BZOJ4825][HNOI2017]单旋(线段树+Splay)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网络互连
- 下一篇: 关于编辑器对input标签报错提示“表单