[HNOI2012]永无乡

题目大意

给出一张\(n\)个点的图,给出初始的联通信息

每个点都有一个独一无二的权值(在\(1 \sim n\)中)

接下来有两种操作:

\(Q\)操作:询问\(A\)所在联通块中权值第\(k\)大(不存在输出\(-1\))

\(B\)操作:联通\(X,Y\)

解题思路

一眼是平衡树裸题呀,但本文选择用动态开点线段树合并做,另外用并查集维护一下连通性

第\(i\)棵线段树维护了\(i\)所在的联通块中权值在\(l \sim r\)的结点总数\(size\)

定义:\(find(x)\)为\(x\)在并查集中的最远祖先(并查集基本操作)

我们约定\(i\)所在的联通块的信息保存在第\(find(i)\)棵线段树上

这意味着一个联通块有唯一确定的线段树在维护它

合并操作:

首先判断\(x,y\)是否已经联通(是否在并查集同一个最远祖先下),若已经联通就跳出

否则合并第\(find(x)\)和\(find(y)\)棵线段树,并合并在\(root[find(x)]\)下(相当于把第\(find(y)\)棵线段树的信息附加到第\(find(x)\)棵上)

然后维护并查集的信息(常规\(Union\)操作)

询问操作:

因为\(A\)所在联通块信息保存在第\(find(A)\)棵线段树上,所以在第\(find(A)\)棵线段树上查找第\(k\)大即可(利用线段树维护的\(size\)信息)

#include<iostream>
#include<cstdio>

int n,m,imp[200000],reimp[200000];
char opt;
int x,y;

int B[200000];

struct node{
    int L,R,size;
}N[10000000];
int tot,root[200000];

void insert(int &st,int k,int l,int r){
    if (l>k||r<k) return;
    if (!st) st=++tot;
    if (l==r){N[st].size=1;return;}
    int mdl=(l+r)>>1;
    insert(N[st].L,k,l,mdl);
    insert(N[st].R,k,mdl+1,r);
    N[st].size=1;
}

int merge(int nowL,int nowR,int l,int r){
    if (!(nowL&&nowR)) return nowL|nowR;
    if (l>r) return 0;
    N[nowL].size+=N[nowR].size;
    int mdl=(l+r)>>1;
    N[nowL].L=merge(N[nowL].L,N[nowR].L,l,mdl);
    N[nowL].R=merge(N[nowL].R,N[nowR].R,mdl+1,r);
    return nowL;
}

int query(int now,int k,int l,int r){
    if (l==r) return reimp[l];
    int rank=N[N[now].L].size;
    int mdl=(l+r)>>1;
    if (k<=rank) return query(N[now].L,k,l,mdl);
    else return query(N[now].R,k-rank,mdl+1,r);
}

int find(int k){return (k==B[k])?(k):(B[k]=find(B[k]));}

int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++){
        scanf("%d",&imp[i]);
        insert(root[i],imp[i],1,n+1);
        reimp[imp[i]]=i;
    }
    for (int i=1;i<=n;i++) B[i]=i;
    for (int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        if (find(x)==find(y)) continue;
        root[find(y)]=merge(root[find(y)],root[find(x)],1,n+1);
        B[find(x)]=find(y);
    }
    scanf("%d\n",&m);
    for (int i=1;i<=m;i++){
        scanf("%c%d%d\n",&opt,&x,&y);
        if (opt=='Q'){
            int ans=query(root[find(x)],y,1,n+1);
            if (!ans) puts("-1");
            else printf("%d\n",ans);
        }
        else{
            if (find(x)==find(y)) continue;
            root[find(y)]=merge(root[find(y)],root[find(x)],1,n+1);
            B[find(x)]=find(y);
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/ytxytx/p/9441019.html