Bzoj 2733: [HNOI2012]永無鄉 數組Splay+啟發式合並
阿新 • • 發佈:2017-09-24
memory clas ring solved script none 通過 接下來 update
Submit: 3955 Solved: 2112
[Submit][Status][Discuss]
4 3 2 5 1
1 2
7
Q 3 2
Q 2 1
B 2 3
B 1 5
Q 2 1
Q 2 4
Q 2 3
2
5
1
2
這道題當時第一眼覺得像是圖論tarjan題,然後看到了求排名的操作,這就不得不考慮平衡樹了,由於平衡樹我只會SPLAY,不會無旋TREAP所以我就只講SPLAY的了。
這道題想到平衡樹後就開始想合並操作,如果暴力合並最壞貌似是炸翻天的,一開始以為有什麽神奇的打法,比如區間插入還自帶平衡的黑科技之類的,結果同桌告訴我是啟發式合並。所謂啟發式合並就是暴力合並,只是有一個幾乎所有人都想的到的剪枝,讓小項去合並大項,時間自然會小不少。
所以對於每一次合並我們只是dfs一邊當前的小子樹,然後就暴力合並就好了,其他操作照常。
2733: [HNOI2012]永無鄉
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 3955 Solved: 2112
[Submit][Status][Discuss]
Description
永無鄉包含 n 座島,編號從 1 到 n,每座島都有自己的獨一無二的重要度,按照重要度可 以將這 n 座島排名,名次用 1 到 n 來表示。某些島之間由巨大的橋連接,通過橋可以從一個島 到達另一個島。如果從島 a 出發經過若幹座(含 0 座)橋可以到達島 b,則稱島 a 和島 b 是連 通的。現在有兩種操作:B x y 表示在島 x 與島 y 之間修建一座新橋。Q x k 表示詢問當前與島 x連通的所有島中第 k 重要的是哪座島,即所有與島 x 連通的島中重要度排名第 k 小的島是哪 座,請你輸出那個島的編號。
Input
輸入文件第一行是用空格隔開的兩個正整數 n 和 m,分別 表示島的個數以及一開始存在的橋數。接下來的一行是用空格隔開的 n 個數,依次描述從島 1 到島 n 的重要度排名。隨後的 m 行每行是用空格隔開的兩個正整數 ai 和 bi,表示一開始就存 在一座連接島 ai 和島 bi 的橋。後面剩下的部分描述操作,該部分的第一行是一個正整數 q, 表示一共有 q 個操作,接下來的 q 行依次描述每個操作,操作的格式如上所述,以大寫字母 Q 或B 開始,後面跟兩個不超過 n 的正整數,字母與數字以及兩個數字之間用空格隔開。 對於 20%的數據 n≤1000,q≤1000
對於 100%的數據 n≤100000,m≤n,q≤300000
Output
對於每個 Q x k 操作都要依次輸出一行,其中包含一個整數,表 示所詢問島嶼的編號。如果該島嶼不存在,則輸出-1。
Sample Input
5 14 3 2 5 1
1 2
7
Q 3 2
Q 2 1
B 2 3
B 1 5
Q 2 1
Q 2 4
Q 2 3
Sample Output
-12
5
1
2
這道題當時第一眼覺得像是圖論tarjan題,然後看到了求排名的操作,這就不得不考慮平衡樹了,由於平衡樹我只會SPLAY,不會無旋TREAP所以我就只講SPLAY的了。
1 #pragma GCC optimze("O3") 2 #include<iostream> 3 #include<cstdlib> 4 #include<cstdio> 5 #include<cstring> 6 #include<queue> 7 #include<algorithm> 8 #include<cmath> 9 #include<map> 10 #define N 100004 11 using namespace std; 12 int n,m,q,f[N],fa[N],size[N],ch[N][2],data[N]; 13 int find(int x) 14 { 15 if(f[x]==x)return x; 16 return f[x]=find(f[x]); 17 } 18 bool get(int x) 19 { 20 return x==ch[fa[x]][1]; 21 } 22 void update(int x) 23 { 24 if(x) 25 { 26 size[x]=1; 27 if(ch[x][0]) size[x]+=size[ch[x][0]]; 28 if(ch[x][1]) size[x]+=size[ch[x][1]]; 29 } 30 } 31 void rotate(int x) 32 { 33 int faa=fa[x],ffa=fa[fa[x]]; 34 int op=get(x); 35 ch[faa][op]=ch[x][op^1]; 36 fa[ch[faa][op]]=faa; 37 ch[x][op^1]=faa; 38 fa[faa]=x; 39 fa[x]=ffa; 40 if(ffa)ch[ffa][ch[ffa][1]==faa]=x; 41 update(faa); 42 update(x); 43 return; 44 } 45 void splay(int x) 46 { 47 for(int ff;ff=fa[x];rotate(x)) 48 { 49 if(fa[ff]) 50 rotate((get(x)==get(ff))?ff:x); 51 } 52 return; 53 } 54 int lart; 55 void insert(int x,int now,int faa) 56 { 57 58 if(now==0) 59 { 60 fa[x]=faa; 61 if(data[x]<data[faa]) ch[faa][0]=x; 62 else ch[faa][1]=x; 63 splay(x); 64 lart=x; 65 return; 66 } 67 if(data[x]<data[now])insert(x,ch[now][0],now); 68 else insert(x,ch[now][1],now); 69 } 70 void dfs(int x,int y) 71 { 72 int le=ch[x][0],ri=ch[x][1]; 73 ch[x][0]=ch[x][1]=0; 74 insert(x,y,0); 75 if(le)dfs(le,lart); 76 if(ri)dfs(ri,lart); 77 } 78 int get_rank(int x,int la) 79 { 80 if(la==size[ch[x][0]]+1) 81 return x; 82 if(la<=size[ch[x][0]]) 83 return get_rank(ch[x][0],la); 84 else 85 return get_rank(ch[x][1],la-size[ch[x][0]]-1); 86 } 87 int main() 88 { 89 scanf("%d%d",&n,&m); 90 for(int i=1;i<=n;i++) 91 { 92 scanf("%d",&data[i]); 93 f[i]=i;size[i]=1; 94 } 95 for(int i=1;i<=m;i++) 96 { 97 int x,y; 98 scanf("%d%d",&x,&y); 99 int aa=find(x),bb=find(y); 100 if(aa==bb)continue; 101 f[aa]=bb; 102 splay(x),splay(y); 103 if(size[x]>size[y]) 104 dfs(y,x); 105 else 106 dfs(x,y); 107 } 108 scanf("%d",&q); 109 char b[10]; 110 for(int i=1;i<=q;i++) 111 { 112 scanf("%s",b); 113 int x,y; 114 scanf("%d%d",&x,&y); 115 if(b[0]==‘B‘) 116 { 117 int aa=find(x),bb=find(y); 118 if(aa==bb)continue; 119 f[aa]=bb; 120 splay(x),splay(y); 121 if(size[x]>size[y]) 122 dfs(y,x); 123 else 124 dfs(x,y); 125 } 126 else 127 { 128 splay(x); 129 if(size[x]<y)printf("-1\n"); 130 else 131 { 132 printf("%d\n",get_rank(x,y)); 133 } 134 } 135 } 136 return 0; 137 }View Code
Bzoj 2733: [HNOI2012]永無鄉 數組Splay+啟發式合並