zoukankan      html  css  js  c++  java
  • 【HNOI2012】永无乡 题解(并查集+线段树合并)

    题目链接

    给定一张含$n$个点$m$条边的无向图,每个点有一个重要指数$a_i$。有两种操作:1.在$x$和$y$之间连一条边;2.求$x$所在连通块中重要程度第$k$小的点。

    ---------------------------------

    维护第$k$小,很容易想到权值线段树。看到合并二字,可以想到用线段树合并的方法。维护连通块可以用并查集做。

    注意并查集合并的方向和线段树合并的方向要一致。查询的时候要先找出并查集的根再查询。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,q,fa[100005],id[10000005],rt[100005],tot;
    struct node
    {
        int ls,rs,sum;
    }tree[10000005];
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline int find(int x)
    {
        if (x==fa[x]) return x;
        return fa[x]=find(fa[x]);
    }
    inline int build(int index,int l,int r,int pos,int idx)
    {
        if (!index) index=++tot;
        if (l==r){id[index]=idx;tree[index].sum++;return index;}
        int mid=(l+r)>>1;
        if (pos<=mid) tree[index].ls=build(tree[index].ls,l,mid,pos,idx);
        else tree[index].rs=build(tree[index].rs,mid+1,r,pos,idx);
        tree[index].sum=tree[tree[index].ls].sum+tree[tree[index].rs].sum;
        return index;
    }
    inline int merge(int x,int y,int l,int r)
    {
        if (!x) return y;
        if (!y) return x;
        if (l==r){if (id[y]){id[x]=id[y];tree[x].sum+=tree[y].sum;}return x;} 
        int mid=(l+r)>>1;
        tree[x].ls=merge(tree[x].ls,tree[y].ls,l,mid);
        tree[x].rs=merge(tree[x].rs,tree[y].rs,mid+1,r);
        tree[x].sum=tree[tree[x].ls].sum+tree[tree[x].rs].sum;
        return x;
    }
    inline int query(int index,int x,int l,int r)
    {
        if (tree[index].sum<x||!index) return 0;
        if (l==r) return id[index];
        int mid=(l+r)>>1,ans;
        if (x<=tree[tree[index].ls].sum) ans=query(tree[index].ls,x,l,mid);
        else ans=query(tree[index].rs,x-tree[tree[index].ls].sum,mid+1,r);
        return ans;
    }
    int main()
    {
        n=read(),m=read();
        for (int i=1;i<=n;i++)
        {
            fa[i]=i;int x=read();
            rt[i]=build(rt[i],1,n,x,i);
        }
        for (int i=1;i<=m;i++)
        {
            int x=read(),y=read();
            x=find(x),y=find(y);
            fa[y]=x;
            rt[x]=merge(rt[x],rt[y],1,n);
        }
        q=read();
        while(q--)
        {
            char c;cin>>c;
            int x=read(),y=read();
            if (c=='B')
            {
                x=find(x),y=find(y);
                if (x==y) continue;
                fa[y]=x;
                rt[x]=merge(rt[x],rt[y],1,n);
            }
            else
            {
                x=find(x);
                int ans=query(rt[x],y,1,n);
                if (!ans) printf("-1
    ");
                else printf("%d
    ",ans); 
            }
        }
        return 0;
    }
  • 相关阅读:
    日常脚本练习与解释
    第五周作业
    centos 7中命令记录
    centos7 中如何查看、打开、关闭防火墙。
    第四周
    复制/etc/profile至/tmp/目录,用查找替换命令删除/tmp/profile文件中的 行首的空白字符
    vim中设置tab缩进为4个字符
    Linux上的文件管理类命令都有哪些,其常用的使用方法及其相关示例演示。
    sql 索引笔记--索引组织结构
    sql 索引笔记2
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13356851.html
Copyright © 2011-2022 走看看