題目描述
永無鄉包含 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\n"); continue; } a=findfa(a); if(siz[rt[a]]<b) { printf("-1\n"); continue; }else { printf("%d\n",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; }