一看题吓傻了根本不会做啊。。然后又看了几遍题,好像就是维护各个联通块的k大嘛。。一开始纠结了好久,后来想起好像没有断边的操作,那就好搞了。。第一次写splay的启发式合并,用splay维护每个联通块的重要度序列,然后可以弄个并查集,如果连边的时候是两个不同联通块,那就把size小的那个splay按中序遍历一个一个插入到size大的那个splay里去,这就是启发式合并。。询问就按findkth那样搞就行了。。
#include<cstdio> #include<iostream> #define N 100005 #define inf 999999999 #define ll long long using namespace std; int n,m,x,y,q,top,i,c[N][2],pre[N],fa[N],size[N],st[N]; ll v[N]; char s[2]; int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);} void update(int x) { size[x]=size[c[x][0]]+size[c[x][1]]+1; } void rot(int x,int kind) { int y=pre[x],z=pre[y]; pre[c[x][kind]]=y;c[y][!kind]=c[x][kind]; pre[y]=x;c[x][kind]=y; pre[x]=z;if (z) c[z][c[z][1]==y]=x; update(y);update(x); } void splay(int x) { int kind,y,z; while (pre[x]) { y=pre[x]; if (!pre[y]) rot(x,c[y][0]==x); else { z=pre[y];kind=c[z][0]==y; if (c[y][kind]==x) rot(x,!kind);else rot(y,kind); rot(x,kind); } } } void ins(int &x,int t) { while(c[x][v[x]<v[t]]) x=c[x][v[x]<v[t]]; c[x][v[x]<v[t]]=t;pre[t]=x; c[t][0]=c[t][1]=0;size[t]=1; x=t;splay(x); } void push(int x) { if (!x) return; push(c[x][0]); st[++top]=x; push(c[x][1]); } void merge(int x,int y) { splay(x);splay(y); if (size[x]<size[y]) swap(x,y); int k=fa[x];top=0; push(y); while (top) ins(x,st[top--]); fa[fa[y]]=k; } int findkth(int x,int k) { if (k>size[x]) return -1; if (k<=size[c[x][0]]) return findkth(c[x][0],k); if (k==size[c[x][0]]+1) return x; return findkth(c[x][1],k-size[c[x][0]]-1); } int main() { freopen("2733.in","r",stdin); scanf("%d%d",&n,&m); for (i=1;i<=n;i++) scanf("%lld",&v[i]),fa[i]=i,pre[i]=0,size[i]=1,c[x][1]=c[x][0]=0; for (i=1;i<=m;i++) { scanf("%d%d",&x,&y); if (getfa(x)!=getfa(y)) merge(x,y); } scanf("%d",&q); while (q--) { scanf("%s%d%d",s,&x,&y); if (s[0]=='B') { if (getfa(x)!=getfa(y)) merge(x,y); } else splay(x),printf("%d\n",findkth(x,y)); } }