一看题吓傻了根本不会做啊。。然后又看了几遍题,好像就是维护各个联通块的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));
}
}