并查集入门题

 POJ 1611

题意:给你各个集合包含的元素,求0所在的集合总共有多少个不相同的元素。
题解:维护一个size即可,注意size是在什么时候进行相加的。

//#include <bits/stdc++.h>
#include <cstdio>
using namespace std;

int fa[30005], size[30005];

int find(int x)
{
    if(fa[x]==x) return x;
    return fa[x]=find(fa[x]);
}

int main()
{
    int n, m;
    while(~scanf("%d%d", &n, &m))
    {
        if(n==0 && m==0) break;
        for(int i=0; i<n; i++)
            fa[i]=i, size[i]=1;
        for(int i=0; i<m; i++)
        {
            int cnt, x, y; 
            scanf("%d", &cnt);
            if(cnt) scanf("%d", &x);
            for(int i=1; i<cnt; i++)
            {
                scanf("%d", &y);
                int px=find(x), py=find(y);
                if(px!=py){
                    size[px]+=size[py];
                    fa[py]=px;    
                }    
            }
        }
        int p=find(0);
        printf("%d\n", size[p]);
    }
    return 0;
} 
View Code

 

POJ 1988 cube stacking

题意: 有N(N<=30,000)堆方块,开始每堆都是一个方块,方块编号1 – N. 有两种操作:
• M x y : 表示把方块x所在的堆,拿起来叠放到y所在的堆上。
• C x : 问方块x下面有多少个方块。
• 操作最多有 P (P<=100,000)次。对每次C操作,输出结果

题解:形成一种树形结构,直接用带权并查集维护,size,under 即可。注意路径压缩的过程中别写错了。

//#include <bits/stdc++.h>
#include <cstdio>
using namespace std;

const int maxn=30005;
int fa[maxn], under[maxn], size[maxn];

int find(int x)
{
    if(fa[x]==x) return x;
    int t=find(fa[x]);
    under[x]+=under[fa[x]]; // 注意是加上under[fa[x]]而不是under[t] 
    return fa[x]=t;
}

void merge(int x, int y)
{
    int px=find(x), py=find(y);
    if(px!=py){ 
        under[py]=size[px];   //注意在合并之前,把所有要维护的都维护掉 
        size[px]+=size[py];
        fa[py]=px; 
    }
}

void init(int n)
{
    for(int i=1; i<=maxn; i++)
    {
        size[i]=1;
        under[i]=0;
        fa[i]=i;
    }
}

int main()
{
    int n; 
    scanf("%d", &n);
    init(n);
    for(int i=0; i<n; i++)
    {
        char cmd[10];
        int x, y;
        scanf("%s", cmd);
        if(cmd[0]=='M'){
            scanf("%d%d", &x, &y);
            merge(y, x);
        }
        else{
            scanf("%d", &x);
            find(x);          //注意要先路径压缩后在直接输出 
            printf("%d\n", under[x]);
        }
    }
    return 0;
}
View Code

 

BZOJ 1202  [HNOI2005] 狡猾的商人

题意:给你多个区间u到v的和为w,问是否会有冲突,每个位置的值可以是负数。
题解:判断时候只有知道了x-y,x-z,才能判断y-z是否冲突。故使用并查集维护区间和,根节点为起点,在同一个并查集内必然可以判断是否冲突。

#include <bits/stdc++.h>

int val[105], fa[105], ok;

int find(int x)
{
    if(fa[x]==x) return x;
    int t=find(fa[x]);
    val[x]+=val[fa[x]];
    return fa[x]=t;
}

void merge(int u, int v, int w)
{
    int pu=find(u), pv=find(v);
    if(pu!=pv){
        val[pv]=-val[v]-w+val[u];
        fa[pv]=pu;
    }
    else{
        if(val[v]-val[u]!=-w) ok=0;
    }
}

int main()
{
    int T;
    for(scanf("%d", &T); T--; )
    {
        int n, m;
        scanf("%d%d", &n, &m);
        for(int i=0; i<=n; i++)
            val[i]=0, fa[i]=i;
        ok=1;
        for(int i=0; i<m; i++)
        {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            merge(u-1, v, w);           //起点往左边移动一位方便计算
        }
        printf("%s\n", ok? "true":"false");
    }
    return 0;
}
View Code

 

posted @ 2019-09-06 15:11  N_Yokel  阅读(188)  评论(0编辑  收藏  举报