• 洛谷P3224 [HNOI2012]永无乡 线段树合并模板



    题意:
    Q 查询与x连通的第k小
    B 将两个集合连通
    做法:
    求集合第k小->值域线段树  集合连通关系->并查集维护  合并集合->线段树合并
    注意:线段树的下标是值域  id存储的是 以线段树节点为下标 题中对应的点标号   
    query中返回的是 那个点的标号   而 l r 是对应的值域
    此题值域小 不需要离散化 一定要分清 点标号 值域  与线段树中的点

    #include<bits/stdc++.h>
    using namespace std;
    #define M 3100005
    #define N 100005
    #define mid ((l+r)>>1)
    int sum[M],id[M],ls[M],rs[M],ndnum=0;
    int rt[N],f[N];
    int get(int x) { if(x==f[x]) return x; return f[x]=get(f[x]); }
    void update(int a) { sum[a]=sum[ls[a]]+sum[rs[a]]; }
    int add(int a,int l,int r,int pos,int idx)
    {
        if(!a) a=++ndnum;//这里是直接新建点 
        if(l==r) { id[a]=idx; sum[a]++; return a; }
        if(pos<=mid) ls[a]=add(ls[a],l,mid,pos,idx);
        else rs[a]=add(rs[a],mid+1,r,pos,idx);
        update(a);
        return a;
    }
    int merge(int a,int b,int l,int r)
    {
        if(!a) return b;//对于不共有的直接返回 
        if(!b) return a;
        if(l==r) { id[a]=id[b],sum[a]+=sum[b]; return a; }//递归到叶子结点说明共有 而共有部分就直接合并信息 
        ls[a]=merge(ls[a],ls[b],l,mid);
        rs[a]=merge(rs[a],rs[b],mid+1,r);
        update(a);
        return a;
    }
    int query(int s,int l,int r,int k)
    {
        int ans;
        if(k>sum[s]||!s) return 0;//特判 
        if(l==r) return id[s];
        if(k<=sum[ls[s]]) ans=query(ls[s],l,mid,k);
        else ans=query(rs[s],mid+1,r,k-sum[ls[s]]);
        return ans;
    }
    int main()
    {
        int n,m,a,b,c,Q;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d",&a),rt[i]=add(rt[i],1,n,a,i);//初始时每个点都有一颗线段树 
        for(int i=1;i<=n;i++) f[i]=i;//并查集一定要记得初始化 
        for(int i=1;i<=m;i++){
            scanf("%d%d",&b,&c);
            int x=get(b),y=get(c);
            f[y]=x;//并查集的合并方向一定要和线段树一样!! 
            rt[x]=merge(rt[x],rt[y],1,n);
        }
        scanf("%d",&Q);
        while(Q--){
            char op[2];
            scanf("%s",op);
            scanf("%d%d",&b,&c);
            if(op[0]=='Q'){
                int fa=get(b);//先找父亲 再找线段树的根!! 
                int ans=query(rt[fa],1,n,c);
                //一定要写rt[fa] 因为集合的代表元素的点在线段树中的标号是通过++ndnum得来的 而不是其本身 
                if(!ans) printf("-1
    ");
                else printf("%d
    ",ans);
            }
            else{
                int fx=get(b),fy=get(c);
                if(fx==fy) continue;
                f[fy]=fx;//找到两个节点的根 合并根的线段树 
                rt[fx]=merge(rt[fx],rt[fy],1,n);
            }
        }
    }
    /*
    5  1
    4  3 2 5 1
    1  2
    7
    Q 3 2
    Q 2 1
    B 2 3
    B 1 5
    Q 2 1
    Q 2 4
    Q 2 3
    */
  • 相关阅读:
    Cheatsheet: 2013 12.01 ~ 12.16
    Cheatsheet: 2013 11.12 ~ 11.30
    Cheatsheet: 2013 11.01 ~ 11.11
    Cheatsheet: 2013 10.24 ~ 10.31
    Cheatsheet: 2013 10.09 ~ 10.23
    Cheatsheet: 2013 10.01 ~ 10.08
    Cheatsheet: 2013 09.22 ~ 09.30
    Cheatsheet: 2013 09.10 ~ 09.21
    Cheatsheet: 2013 09.01 ~ 09.09
    Cheatsheet: 2013 08.20 ~ 08.31
  • 原文地址:https://www.cnblogs.com/mowanying/p/11235951.html
Copyright © 2020-2023  润新知