日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[BZOJ4825][HNOI2017]单旋(线段树+Splay)

發布時間:2024/8/26 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [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
5

Sample 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 }

?

轉載于:https://www.cnblogs.com/HocRiser/p/8299273.html

總結

以上是生活随笔為你收集整理的[BZOJ4825][HNOI2017]单旋(线段树+Splay)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。