HNOI2012 永无乡
这道题一开始看……能想出来用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--) #define enter 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; }
当你意识到,每个上一秒都成为永恒。