[bzoj2733][HNOI2012]永无乡

来源:互联网 发布:手机主板检测软件 编辑:程序博客网 时间:2024/04/30 12:15

题目大意

有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;            }        }    }}
0 0