您的位置:首页 > 其它

[bzoj2733][HNOI2012]永无乡

2016-06-26 10:23 281 查看

题目大意

有n个点,初始时有一些边。

每次操作要么加一条边,要么询问一个点所在联通块数值第k大的点。

水题

联通情况并查集维护,每个连通块再对应一个权值线段树。

合并连通块就是线段树合并。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10;
int tree[maxn*20],left[maxn*20],right[maxn*20];
int a[maxn],b[maxn],fa[maxn],root[maxn];
int i,j,k,l,t,n,m,tot;
char ch;
int read(){
int x=0;
char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x;
}
char get(){
char ch=getchar();
while (ch!='Q'&&ch!='B') ch=getchar();
return ch;
}
int getfa(int x){
return fa[x]?fa[x]=getfa(fa[x]):x;
}
void insert(int &x,int l,int r,int a){
if (!x) x=++tot;
tree[x]++;
if (l==r) return;
int mid=(l+r)/2;
if (a<=mid) insert(left[x],l,mid,a);else insert(right[x],mid+1,r,a);
}
int merge(int a,int b,int l,int r){
if (!a||!b) return a+b;
int mid=(l+r)/2;
left[a]=merge(left[a],left[b],l,mid);
right[a]=merge(right[a],right[b],mid+1,r);
tree[a]+=tree[b];
return a;
}
int query(int x,int l,int r,int k){
if (l==r) return l;
int mid=(l+r)/2;
if (tree[left[x]]>=k) return query(left[x],l,mid,k);else return query(right[x],mid+1,r,k-tree[left[x]]);
}
int main(){
scanf("%d%d",&n,&m);
fo(i,1,n){
scanf("%d",&a[i]);
b[a[i]]=i;
insert(root[i],1,n,a[i]);
}
fo(i,1,m){
scanf("%d%d",&j,&k);
j=getfa(j);
k=getfa(k);
if (j!=k){
root[j]=merge(root[j],root[k],1,n);
fa[k]=j;
}
}
scanf("%d",&m);
fo(i,1,m){
ch=get();
scanf("%d%d",&j,&k);
if (ch=='Q'){
j=getfa(j);
if (tree[root[j]]<k) printf("-1\n");else printf("%d\n",b[query(root[j],1,n,k)]);
}
else{
j=getfa(j);
k=getfa(k);
if (j!=k){
root[j]=merge(root[j],root[k],1,n);
fa[k]=j;
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: