zoukankan      html  css  js  c++  java
  • 洛谷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
    */
  • 相关阅读:
    PAT 甲级 1126 Eulerian Path (25 分)
    PAT 甲级 1126 Eulerian Path (25 分)
    PAT 甲级 1125 Chain the Ropes (25 分)
    PAT 甲级 1125 Chain the Ropes (25 分)
    PAT 甲级 1124 Raffle for Weibo Followers (20 分)
    PAT 甲级 1124 Raffle for Weibo Followers (20 分)
    PAT 甲级 1131 Subway Map (30 分)
    PAT 甲级 1131 Subway Map (30 分)
    AcWing 906. 区间分组 区间贪心
    AcWing 907. 区间覆盖 区间贪心
  • 原文地址:https://www.cnblogs.com/mowanying/p/11235951.html
Copyright © 2011-2022 走看看