题目:BZOJ2733、洛谷P3324、codevs1477
题目大意:给你n座岛,每座岛一个权值,某些岛是连在一起的。
有q个操作,每个操作可以:
1.将两座岛连起来。
2.输出和x岛直接或间接连接的所有岛里,权值第y小的岛的编号。
解题思路:线段树合并。这是我写的第一道线段树合并题。听说可以用高大上的“splay启发式合并”写,然而我并不会。orz
动态开点,给线段树留的内存不能太小,否则会炸掉。
C++ Code:
#include<cstdio> using namespace std; int n,m,fa[100005],v[100005],num[100005],root[100005],node=0,d[100005*22],ld[100005*22],rd[100005*22]; int dad(int x){return fa[x]==x?x:fa[x]=dad(fa[x]);} void make(int& k,int l,int r,int p){ if(!k)k=++node; if(l==r){ d[k]=1;return; } int m=(l+r)>>1; if(p<=m)make(ld[k],l,m,p);else make(rd[k],m+1,r,p); d[k]=d[ld[k]]+d[rd[k]]; } int merge(int x,int y){ if(!x||!y)return x^y; ld[x]=merge(ld[x],ld[y]); rd[x]=merge(rd[x],rd[y]); d[x]=d[ld[x]]+d[rd[x]]; return x; } int query(int k,int l,int r,int p){ if(l==r)return l; int m=(l+r)>>1; if(d[ld[k]]>=p)return query(ld[k],l,m,p); return query(rd[k],m+1,r,p-d[ld[k]]); } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d",&v[i]); fa[i]=i; num[v[i]]=i; } while(m--){ int x,y; scanf("%d%d",&x,&y); int a=dad(x),b=dad(y); fa[b]=a; } for(int i=1;i<=n;i++){ make(root[dad(i)],1,n,v[i]); } char c[3]; scanf("%d",&m); while(m--){ int x,y; scanf("%s%d%d",c,&x,&y); if(c[0]=='B'){ int p=dad(x),q=dad(y); if(p!=q){ fa[q]=p; root[q]=merge(root[p],root[q]); } }else{ int p=dad(x); if(d[root[p]]<y)puts("-1");else printf("%d ",num[query(root[p],1,n,y)]); } } return 0; }