[BZOJ2733][HNOI2012] 永无乡(线段树合并)

题面

永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的。现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥。Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输出那个岛的编号。

分析

乍一眼看上去像平衡树的合并

但实际上用权值线段树合并即可

用并查集维护连通性,同时对于每个联通块维护一个权值线段树,权值线段树维护的序列的第i个位置为1表示当前联通块里有权值为i的节点,线段树维护区间和,代表联通块里值为[l,r]之间的数的个数

合并的时候就直接合并两个联通块对应的线段树节点

查询的时候类似在线段树上二分,每次根据区间和决定向左子树还是右子树递归。如果左子树的和>=rank,则说明第rank重要的节点在右子树里

代码

#include<iostream>
#include<cstdio>
#define maxn 100005 
#define maxlogn 18
using namespace std;
int n,m,q;
int a[maxn];
int id[maxn];
int fa[maxn];
int find(int x){
    if(fa[x]==x) return x;
    else return fa[x]=find(fa[x]);
}
void merge_set(int x,int y){
    fa[find(x)]=find(y);
}

int ptr=0; 
int root[maxn*maxlogn];
struct node{
    int ls;
    int rs;
    int val;
}tree[maxn*maxlogn]; 
void push_up(int pos){
    tree[pos].val=tree[tree[pos].ls].val+tree[tree[pos].rs].val; 
} 
void insert(int &pos,int l,int r,int val){
    if(pos==0) pos=++ptr;
    if(l==r){
        tree[pos].val=1;
        return;
    }
    int mid=(l+r)>>1;
    if(val<=mid) insert(tree[pos].ls,l,mid,val);
    else insert(tree[pos].rs,mid+1,r,val);
    push_up(pos); 
}
int query(int pos,int l,int r,int val){
    if(l==r) return l;
    int mid=(l+r)>>1;
    if(tree[tree[pos].ls].val>=val) return query(tree[pos].ls,l,mid,val);
    else return query(tree[pos].rs,mid+1,r,val-tree[tree[pos].ls].val);//记得减掉左子树的和
}
int merge_tree(int x,int y){
    if(!x) return y;
    if(!y) return x;
    tree[x].ls=merge_tree(tree[x].ls,tree[y].ls);
    tree[x].rs=merge_tree(tree[x].rs,tree[y].rs);
    push_up(x);
    return x;
}
int main(){
    int u,v;
    char cmd[2];
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=m;i++){
        scanf("%d %d",&u,&v);
        merge_set(u,v); 
    }
    for(int i=1;i<=n;i++){
        insert(root[find(i)],1,n,a[i]);
        id[a[i]]=i;
    }
    scanf("%d",&q);
    for(int i=1;i<=q;i++){
        scanf("%s",cmd);
        if(cmd[0]=='Q'){
            scanf("%d %d",&u,&v);
            if(tree[root[find(u)]].val<v) printf("-1\n");
            else printf("%d\n",id[query(root[find(u)],1,n,v)]);
        }else{
            scanf("%d %d",&u,&v);
            int fu=find(u),fv=find(v);
            if(fu!=fv){
                root[fv]=merge_tree(root[fu],root[fv]);//是把fu并到fv
                fa[fu]=fv;
            }
            
        }
    }
}

posted @ 2019-05-06 22:20  birchtree  阅读(140)  评论(0编辑  收藏  举报