传说中的启发式合并,就是选出n1logn2和n2logn1中的较小值(不要跟我提常数谢谢)
用平衡树维护一个联通块,我选的SBT(好高端的样子,煞笔树吗?),然后就是俩操作了:合并两棵树,查询一棵树内第k小的节点。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=100000+5; inline int read(){ int x=0,f=1;char ch; while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int fa[N],pa[N],sz[N],lc[N],rc[N],key[N]; inline int find(int x){ return pa[x]!=x?pa[x]=find(pa[x]):x; } inline void pushup(int x){sz[x]=sz[lc[x]]+sz[rc[x]]+1;} inline int findroot(int x){ while(fa[x])x=fa[x]; return x; } inline void lturn(int &x){ int k=rc[x];fa[lc[k]]=x;fa[k]=fa[x];fa[x]=k;rc[x]=lc[k];lc[k]=x;sz[k]=sz[x];pushup(x);x=k; } inline void rturn(int &x){ int k=lc[x];fa[rc[k]]=x;fa[k]=fa[x];fa[x]=k;lc[x]=rc[k];rc[k]=x;sz[k]=sz[x];pushup(x);x=k; } inline void maintain(int &x,bool flag){ if(!flag){ if(sz[lc[lc[x]]]>sz[rc[x]])rturn(x); else if(sz[rc[lc[x]]]>sz[rc[x]]){ lturn(lc[x]); rturn(x); }else return; }else{ if(sz[rc[rc[x]]]>sz[lc[x]])lturn(x); else if(sz[lc[rc[x]]]>sz[lc[x]]){ rturn(rc[x]); lturn(x); }else return; } maintain(lc[x],false);maintain(rc[x],true); maintain(x,true);maintain(x,false); } void ins(int &u,int v,int last){ if(!u){u=v;lc[u]=rc[u]=0;sz[u]=1;fa[u]=last;} else{ sz[u]++; if(key[v]<key[u])ins(lc[u],v,u); else ins(rc[u],v,u); maintain(u,key[v]>=key[u]); } } void mergeto(int src,int &dest){ if(lc[src])mergeto(lc[src],dest); if(rc[src])mergeto(rc[src],dest); ins(dest,src,0); } void merge(int u,int v){ u=find(u);v=find(v); if(u==v)return; int rootu=findroot(u),rootv=findroot(v); if(sz[rootu]<sz[rootv]){pa[u]=v;mergeto(rootu,rootv);} else {pa[v]=u;mergeto(rootv,rootu);} } int kth(int u,int k){ int r=sz[lc[u]]+1; if(r==k)return u; else if(k<r)return kth(lc[u],k); else return kth(rc[u],k-r); } int main(){ int n,m;n=read();m=read(); int u,v; for(int i=1;i<=n;i++)pa[i]=i,key[i]=read(),sz[i]=1; for(int i=1;i<=m;i++){ u=read();v=read(); merge(u,v); } int q=read();char opt[10]; while(q--){ scanf("%s%d%d",opt,&u,&v); if(opt[0]=='B')merge(u,v); else{ if(sz[findroot(u)]<v)printf("-1\n"); else printf("%d\n",kth(findroot(u),v)); } } return 0; }