• UVA 11987


    受教了,感谢玉斌大神的博客。

    这道题最难的地方就是操作2,将一个集合中的一个点单独移到另一个集合,因为并查集的性质,如果该点本身作为root节点的话,怎么保证其他点不受影响。

    玉斌大神的思路很厉害,受教受教,即,由于题目最终输出集合的元素个数与权值总和,故添加一个delete操作,将该点(设为P)所在集合的rank和sum值减小,将p的father引向一个从没定义过的点,(可以设置为(总数++)点),这样,虽然看似P还留在原集合,但仅仅作为一个空骨架,并不对集合的rank和sum产生影响。

    具体实现,需要借助一个辅助数组 id[], id[]初始和father[]相同,但一旦需要删除操作,即将id[p]=++n,指向一个新位置,下次father[id[x]]即指向了新位置,跟原集合无关了。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    int father[200005]; //由于最坏可能有1000000次删除操作,故最大的数组量
    int rank[200005];
    long long sum[200005];
    int id[200005];
    int n;
    int cnt;
    void init()
    {
        for(int i=1;i<=n;i++)
        {
            father[i]=i;
            rank[i]=1;
            sum[i]=i;
            id[i]=i;
        }
        cnt=n;
    }
    int findset(int x)
    {
        if (x!=father[x])
        {
            father[x]=findset(father[x]);
        }
        return father[x];
    }
    void unionset(int x,int y)
    {
        x=id[x];
        y=id[y]; //所有点的指向,全部通过一个间接的id[x]的值来指向相应的值 即将所有点全部id化,不再已原来的本身值为id。
        int r1=findset(x);
        int r2=findset(y);
        if (r1==r2) return;
        if (rank[r1]>rank[r2])
        {
            father[r2]=r1;
            rank[r1]+=rank[r2];
            sum[r1]+=sum[r2];
        }
        else
        {
            father[r1]=r2;
            rank[r2]+=rank[r1];
            sum[r2]+=sum[r1];
        }
    }
    void q1()
    {
        int p,q;
        scanf("%d %d",&p,&q);
        unionset(p,q);
    }
    void dele(int x)
    {
        int r=findset(id[x]);
        rank[r]--;
        sum[r]-=x;
        id[x]=++cnt;
        father[id[x]]=id[x];
        rank[id[x]]=1;
        sum[id[x]]=x;
    }
    void q2()
    {
        int p,q;
        scanf("%d %d",&p,&q);
    int    r1=findset(id[p]);
    int    r2=findset(id[q]);
        if (r1==r2) return;
        dele(p);
        unionset(p,q);
    }
    void q3()
    {
        int p;
        scanf("%d",&p);
        int root=findset(id[p]);
        printf("%d %lld
    ",rank[root],sum[root]);
    }
    int main()
    {
        int q;
        while (scanf("%d %d",&n,&q)!=EOF)
        {
            init();
            int i,j,k;
            for (i=1;i<=q;i++)
            {
                int deno;
                scanf("%d",&deno);
                if (deno==1) q1();
                if (deno==2) q2();
                if (deno==3) q3();
            }
        }
        return 0;
    }
  • 相关阅读:
    Asp.net 中 listbox 滚动条 定位 火麒
    Silverlight跨域访问WCF服务 火麒
    网页加载速度优化技巧
    很漂亮的login
    ireport
    方法1,定位,相当于四周受力
    方法2,平移
    大端小端数据存储方式
    c++中RTTI
    C语言细节数组a+1和&a+1
  • 原文地址:https://www.cnblogs.com/kkrisen/p/3250142.html
Copyright © 2020-2023  润新知