bzoj 2733: [HNOI2012]永无乡
Description
永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的。现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥。Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输出那个岛的编号。
Solution
初学线段树合并,此题中开一棵值域,由于此题中每一个节点在遍历时只会被合并一次,也就是说只有一次是被 \(O(log)\) 的遍历到,所以均摊复杂度 \(O(nlogn)\)
线段树合并模板:
int merge(int x,int y){
if(!x)return y;if(!y)return x;
t[x].ls=merge(t[x].ls,t[y].ls);
t[x].rs=merge(t[x].rs,t[y].rs);
t[x].sum=t[t[x].ls].sum+t[t[x].rs].sum;
return x;
}
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N=100005;
struct node{
int ls,rs,sum;
}t[N*20];
int n,m,Q,root[N],val[N],b[N],num=0,fa[N],cnt=0,totnode=0;
char s[3];int id[N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void ins(int &rt,int l,int r,int sa){
if(!rt)rt=++totnode;
if(l==r){t[rt].sum++;return ;}
int mid=(l+r)>>1;
if(sa<=mid)ins(t[rt].ls,l,mid,sa);
else ins(t[rt].rs,mid+1,r,sa);
t[rt].sum=t[t[rt].ls].sum+t[t[rt].rs].sum;
}
int query(int rt,int k,int l,int r){
if(l==r)return l;
int mid=(l+r)>>1;
if(t[t[rt].ls].sum>=k)return query(t[rt].ls,k,l,mid);
return query(t[rt].rs,k-t[t[rt].ls].sum,mid+1,r);
}
int merge(int x,int y){
if(!x)return y;if(!y)return x;
t[x].ls=merge(t[x].ls,t[y].ls);
t[x].rs=merge(t[x].rs,t[y].rs);
t[x].sum=t[t[x].ls].sum+t[t[x].rs].sum;
return x;
}
void work()
{
int x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&val[i]),b[++num]=val[i],fa[i]=i;
sort(b+1,b+num+1);
for(int i=1;i<=n;i++)val[i]=lower_bound(b+1,b+num+1,val[i])-b;
for(int i=1;i<=n;i++)ins(root[i],1,n,val[i]),id[val[i]]=i;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
if(find(x)!=find(y)){
root[find(x)]=merge(root[find(x)],root[find(y)]);
fa[find(y)]=find(x);
}
}
scanf("%d",&Q);
while(Q--){
scanf("%s%d%d",s,&x,&y);
if(s[0]=='Q'){
if(t[root[find(x)]].sum<y)puts("-1");
else printf("%d\n",id[query(root[find(x)],y,1,n)]);
}
else {
root[find(x)]=merge(root[find(x)],root[find(y)]);
fa[find(y)]=find(x);
}
}
}
int main(){work();return 0;}