HNOI2012 永無鄉
阿新 • • 發佈:2018-10-03
struct spa noi2012 ger swap 行合並 %s pri names
傳送門
這道題一開始看……能想出來用splay和並查集維護,不過,怎麽把兩棵splay合並呢……?暴力拆開一個一個合並?
後來發現真的是這樣……不過其實是啟發式合並,也就是每次我們合並兩棵splay的時候,總是把小的那棵合並到大的那棵上面。這樣的話就能保證每個點最多之被合並logn次(別問我為啥,我也不知道)
具體的操作就是,我們在小的那棵splay上進行dfs,如果這個節點有左/右兒子就向下走,然後無路可走的時候就把它insert到大的那棵splay裏面。其他的一切操作都很熟悉。然後每次用並查集維護,註意這次的splay操作都是要從每個節點自己對應的root開始的。
不知道為什麽,我的代碼會MLE/RE一個點。所以最後還是抄了yyb大神的代碼才過的……
看一下代碼吧(90pts)
// luogu-judger-enable-o2 #include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> #include<cstring> #include<vector> #include<set> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #defineenter putchar(‘\n‘) #define de putchar(‘#‘) #define pr pair<int,int> #define mp make_pair #define fi first #define sc second #define pb push_back using namespace std; typedef long long ll; const int M = 4000005; const int N = 10000005; const int INF = 1000000009; int read() { int ans = 0,op = 1; char ch = getchar();while(ch < ‘0‘ || ch > ‘9‘) { if(ch == ‘-‘) op = -1; ch = getchar(); } while(ch >=‘0‘ && ch <= ‘9‘) { ans *= 10; ans += ch - ‘0‘; ch = getchar(); } return ans * op; } struct node { int ch[2],fa,son,val; }t[M]; int n,m,f[M],root[M],tot,idx,num[M],x,y,q; char s[10]; int getfa(int x) { return (x == f[x]) ? x : f[x] = getfa(f[x]); } bool get(int x) { return t[t[x].fa].ch[1] == x; } void pushup(int x) { t[x].son = t[t[x].ch[0]].son + t[t[x].ch[1]].son + 1; } void rotate(int x) { int y = t[x].fa,z = t[y].fa,k = get(x); t[z].ch[get(y)] = x,t[x].fa = z; t[y].ch[k] = t[x].ch[k^1],t[t[y].ch[k]].fa = y; t[x].ch[k^1] = y,t[y].fa = x; pushup(x),pushup(y); } void splay(int x,int goal) { while(t[x].fa != goal) { int y = t[x].fa,z = t[y].fa; if(z != goal) (t[y].ch[0] == x) ^ (t[z].ch[0] == y) ? rotate(x) :rotate(y); rotate(x); } if(goal <= n) root[goal] = x; } void insert(int x,int b) { int u = root[b],f = b; while(u && x != t[u].val) f = u,u = t[u].ch[x > t[u].val]; u = ++tot; if(f > n) t[f].ch[x > t[f].val] = u; t[u].son = 1; t[u].ch[0] = t[u].ch[1] = 0; t[u].fa = f,t[u].val = x; splay(u,b); } void dfs(int x,int g) { if(t[x].ch[0]) dfs(t[x].ch[0],g); if(t[x].ch[1]) dfs(t[x].ch[1],g); insert(t[x].val,g); } void merge(int x,int y) { int r1 = getfa(x),r2 = getfa(y); if(r1 == r2) return; if(t[root[r1]].son > t[root[r2]].son) swap(r1,r2); f[r1] = r2; dfs(root[r1],r2); } int rk(int x,int k) { int u = root[x]; if(t[u].son < k) return -1; while(1) { int y = t[u].ch[0]; if(t[y].son + 1 < k) k -= (t[y].son + 1),u = t[u].ch[1]; else if(t[y].son >= k) u = y; else return t[u].val; } } int main() { n = read(),m = read(); rep(i,1,n) { root[i] = i+n,f[i] = i; x = read(),num[x] = i; t[i+n].val = x,t[i+n].son = 1,t[i+n].fa = i; } tot = n << 1; rep(i,1,m) x = read(),y = read(),merge(x,y); q = read(); while(q--) { scanf("%s",s); x = read(),y = read(); if(s[0] == ‘B‘) merge(x,y); else { int g = rk(getfa(x),y); (g == -1) ? printf("-1\n") : printf("%d\n",num[g]); } } return 0; }
yyb大神的AC代碼:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<queue> #include<vector> #include<algorithm> using namespace std; #define MAX 500000 inline int read() { register int x=0,t=1; register char ch=getchar(); while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘))ch=getchar(); if(ch==‘-‘){t=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-48;ch=getchar();} return x*t; } struct Node { int ch[2]; int val,ff,size; }t[MAX]; int f[MAX]; int root[MAX],tot; int hh[MAX]; int N,M; int getf(int x) { return x==f[x]?x:f[x]=getf(f[x]); } inline void pushup(int x) { t[x].size=t[t[x].ch[0]].size+t[t[x].ch[1]].size+1; } //1..N分別為N棵splay的0節點 //每次都對splay進行合並 inline void rotate(int x) { int y=t[x].ff; int z=t[y].ff; int k=t[y].ch[1]==x; t[z].ch[t[z].ch[1]==y]=x;t[x].ff=z; t[y].ch[k]=t[x].ch[k^1];t[t[x].ch[k^1]].ff=y; t[x].ch[k^1]=y;t[y].ff=x; pushup(y);pushup(x); } inline void splay(int x,int goal) { while(t[x].ff!=goal) { int y=t[x].ff,z=t[y].ff; if(z!=goal) (t[z].ch[0]==y)^(t[y].ch[0]==x)?rotate(x):rotate(y); rotate(x); } if(goal<=N)root[goal]=x;//如果是某一個0節點的下方,則更新當前splay的根節點 } inline void insert(int x,int bh) { int u=root[bh],ff=bh; while(u&&t[u].val!=x) ff=u,u=t[u].ch[x>t[u].val]; u=++tot; t[u].size=1; t[u].ff=ff; if(ff>N) t[ff].ch[x>t[ff].val]=u; t[u].val=x;t[u].ch[0]=t[u].ch[1]=0; splay(u,bh); } void DFS(int u,int kk)//遍歷整顆splay { if(t[u].ch[0])DFS(t[u].ch[0],kk); if(t[u].ch[1])DFS(t[u].ch[1],kk); insert(t[u].val,kk);//合並到另外一顆splay中 } inline void Merge(int a,int b) { int x=getf(a),y=getf(b); if(x==y)return;//已經在一個集合內 if(t[root[x]].size>t[root[y]].size)swap(x,y);//強制將小的合並到大的 f[x]=y; DFS(root[x],y); } int kth(int bh,int k) { int u=root[bh]; if(t[u].size<k)return -1; while(233) { if(t[t[u].ch[0]].size+1<k)//在右子樹中找 { k-=t[t[u].ch[0]].size+1; u=t[u].ch[1]; } else if(t[t[u].ch[0]].size>=k)//在左子樹中找 u=t[u].ch[0]; else return t[u].val;//當前節點 } } int main() { N=read();M=read(); for(int i=1;i<=N;++i)root[i]=i+N,f[i]=i; tot=N+N; for(int i=1;i<=N;++i) { int x=read(); hh[x]=i; t[i+N].val=x;t[i+N].size=1;t[i+N].ff=i; } for(int i=1;i<=M;++i) { int x=read(),y=read(); Merge(x,y); } int Q=read(); while(Q--) { char ch[3];int a,b; scanf("%s",ch);a=read(),b=read(); if(ch[0]==‘B‘) { Merge(a,b); } else { int ans=kth(getf(a),b); printf("%d\n",ans==-1?ans:hh[ans]); } } return 0; }
HNOI2012 永無鄉