【HNOI2012】永无乡 题解(并查集+线段树合并)
给定一张含$n$个点$m$条边的无向图,每个点有一个重要指数$a_i$。有两种操作:1.在$x$和$y$之间连一条边;2.求$x$所在连通块中重要程度第$k$小的点。
---------------------------------
维护第$k$小,很容易想到权值线段树。看到合并二字,可以想到用线段树合并的方法。维护连通块可以用并查集做。
注意并查集合并的方向和线段树合并的方向要一致。查询的时候要先找出并查集的根再查询。
代码:
#include<bits/stdc++.h> using namespace std; int n,m,q,fa[100005],id[10000005],rt[100005],tot; struct node { int ls,rs,sum; }tree[10000005]; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } inline int find(int x) { if (x==fa[x]) return x; return fa[x]=find(fa[x]); } inline int build(int index,int l,int r,int pos,int idx) { if (!index) index=++tot; if (l==r){id[index]=idx;tree[index].sum++;return index;} int mid=(l+r)>>1; if (pos<=mid) tree[index].ls=build(tree[index].ls,l,mid,pos,idx); else tree[index].rs=build(tree[index].rs,mid+1,r,pos,idx); tree[index].sum=tree[tree[index].ls].sum+tree[tree[index].rs].sum; return index; } inline int merge(int x,int y,int l,int r) { if (!x) return y; if (!y) return x; if (l==r){if (id[y]){id[x]=id[y];tree[x].sum+=tree[y].sum;}return x;} int mid=(l+r)>>1; tree[x].ls=merge(tree[x].ls,tree[y].ls,l,mid); tree[x].rs=merge(tree[x].rs,tree[y].rs,mid+1,r); tree[x].sum=tree[tree[x].ls].sum+tree[tree[x].rs].sum; return x; } inline int query(int index,int x,int l,int r) { if (tree[index].sum<x||!index) return 0; if (l==r) return id[index]; int mid=(l+r)>>1,ans; if (x<=tree[tree[index].ls].sum) ans=query(tree[index].ls,x,l,mid); else ans=query(tree[index].rs,x-tree[tree[index].ls].sum,mid+1,r); return ans; } int main() { n=read(),m=read(); for (int i=1;i<=n;i++) { fa[i]=i;int x=read(); rt[i]=build(rt[i],1,n,x,i); } for (int i=1;i<=m;i++) { int x=read(),y=read(); x=find(x),y=find(y); fa[y]=x; rt[x]=merge(rt[x],rt[y],1,n); } q=read(); while(q--) { char c;cin>>c; int x=read(),y=read(); if (c=='B') { x=find(x),y=find(y); if (x==y) continue; fa[y]=x; rt[x]=merge(rt[x],rt[y],1,n); } else { x=find(x); int ans=query(rt[x],y,1,n); if (!ans) printf("-1\n"); else printf("%d\n",ans); } } return 0; }