题目描述
永无乡包含 n 座岛,编号从 1 到 n ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以 到达岛 b ,则称岛 a 和岛 b 是连通的。
现在有两种操作:
B x y 表示在岛 x 与岛 y 之间修建一座新桥。
Q x k 表示询问当前与岛 x 连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪座,请你输出那个岛的编号。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
题解:线段树合并。对每个点开一棵权值线段树,用并查集维护联通性。
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 100050 int n,m,q; int fa[N],ch[70*N][2],siz[70*N],tot,rt[N]; char s[10]; int findfa(int x) { if(fa[x]==x)return x; return fa[x]=findfa(fa[x]); } void update(int x) { siz[x]=siz[ch[x][0]]+siz[ch[x][1]]; } int ans[N]; void merge(int &x,int y,int l,int r) { if(!y)return ; if(!x){x=y;return ;} if(l==r){siz[x]=siz[y];return ;} int mid = (l+r)>>1; merge(ch[x][0],ch[y][0],l,mid); merge(ch[x][1],ch[y][1],mid+1,r); update(x); } void insert(int l,int r,int &u,int x) { u=++tot; siz[u]=1; if(l==r){return ;} int mid = (l+r)>>1; if(x<=mid)insert(l,mid,ch[u][0],x); else insert(mid+1,r,ch[u][1],x); } int query(int l,int r,int u,int k) { if(l==r)return ans[l]; int tmp = siz[ch[u][0]]; int mid = (l+r)>>1; if(tmp>=k)return query(l,mid,ch[u][0],k); else return query(mid+1,r,ch[u][1],k-tmp); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)fa[i]=i; for(int x,i=1;i<=n;i++) { scanf("%d",&x); insert(1,n,rt[i],x); ans[x]=i; } for(int u,v,i=1;i<=m;i++) { scanf("%d%d",&u,&v); int f1 = findfa(u),f2 = findfa(v); if(f1!=f2) { fa[f2]=f1; merge(rt[f1],rt[f2],1,n); } } scanf("%d",&q); for(int a,b,i=1;i<=q;i++) { scanf("%s%d%d",s+1,&a,&b); if(s[1]=='Q') { if(a>n) { printf("-1 "); continue; } a=findfa(a); if(siz[rt[a]]<b) { printf("-1 "); continue; }else { printf("%d ",query(1,n,rt[a],b)); } }else { int f1 = findfa(a),f2 = findfa(b); if(f1!=f2) { fa[f2]=f1; merge(rt[f1],rt[f2],1,n); } } } return 0; }