zoukankan      html  css  js  c++  java
  • [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
    ");
                else printf("%d
    ",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;
                }
                
            }
        }
    }
    
    
  • 相关阅读:
    数据结构与算法简记--拓扑排序
    数据结构与算法简记--redis有序集合数据结构-跳表
    数据结构与算法简记--动态规划实战
    数据结构与算法简记--动态规划理论
    数据结构与算法简记--动态规划初识
    数据结构与算法简记--回溯算法
    数据结构与算法简记--分治算法
    数据结构与算法简记--贪心算法
    数据结构与算法简记--搜索算法
    数据结构与算法简记--图
  • 原文地址:https://www.cnblogs.com/birchtree/p/10822564.html
Copyright © 2011-2022 走看看