离线版
首先将询问和加边一起排个序
对每个节点建一棵主席树,连边时主席树合并即可
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<stack> #include<cmath> using namespace std; typedef long long ll; const int maxn = 1000010; int n,m,q; int a[maxn],b[maxn],fa[maxn],rt[maxn],ans[maxn],p,tot; struct Node{ int sz,ls,rs; }t[maxn<<2]; struct Q{ int a,b,c,ok,id; }c[maxn<<2]; int find(int x){ return x==fa[x]?x:fa[x]=find(fa[x]); } bool cmp(Q a,Q b){ if(a.c==b.c) return a.ok<b.ok; // 询问和添加一起排序,并且保证询问前添加已全部完成 return a.c<b.c; } void modify(int &i,int l,int r,int p){ if(!i) i=++tot; // 如果原来存在主席树节点就用原来的 t[i].sz=1; if(l==r) return; int mid=(l+r)/2; if(p<=mid) modify(t[i].ls,l,mid,p); else modify(t[i].rs,mid+1,r,p); } int query(int i,int l,int r,int k){ if(l==r) return l; int sum=t[t[i].ls].sz; int mid=(l+r)/2; if(k<=sum) return query(t[i].ls,l,mid,k); else return query(t[i].rs,mid+1,r,k-sum); } int merge(int u,int v){ // 线段树合并 if(!u||!v) return u|v; else if(!t[u].ls&&!t[u].rs){ t[u].sz+=t[v].sz; return u; }else{ t[u].ls=merge(t[u].ls,t[v].ls); t[u].rs=merge(t[u].rs,t[v].rs); t[u].sz=t[u].sz+t[v].sz; return u; } } ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f;} int main(){ n=read(),m=read(),q=read(); for(int i=1;i<=n;i++) a[i]=read(),b[i]=a[i],fa[i]=i; sort(b+1,b+1+n); p=unique(b+1,b+1+n)-b-1; for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+p,a[i])-b; for(int i=1;i<=m;i++) c[i].a=read(),c[i].b=read(),c[i].c=read(),c[i].ok=0; for(int i=m+1;i<=m+q;i++) c[i].a=read(),c[i].c=read(),c[i].b=read(),c[i].ok=1,c[i].id=i-m; sort(c+1,c+1+m+q,cmp); for(int i=1;i<=n;i++) modify(rt[i],1,p,a[i]); for(int i=1;i<=m+q;i++){ if(c[i].ok==0){ int u=find(c[i].a),v=find(c[i].b); if(u!=v){ fa[u]=v; rt[v]=merge(rt[u],rt[v]); } }else{ int u=find(c[i].a); if(t[rt[u]].sz<c[i].b) ans[c[i].id]=-1; else ans[c[i].id]=b[query(rt[u],1,p,t[rt[u]].sz-c[i].b+1)]; // 第 k 大 } } for(int i=1;i<=q;i++){ printf("%d\n",ans[i]); } return 0; }