【模版】并查集(及其加权)


并查集


并查集是经典的图论算法,用来维护点与集合的关系,代码也简洁明了。

给定 N N N 个点,有 M M M 次操作,每次操作输入 p p p a a a b b b
p = 1 p = 1 p=1 ,则 合并 a a a b b b
p = 2 p = 2 p=2 ,则 查询 a a a b b b 是否同属一个集合;

并查集初始化:每个父亲点 都 存储 儿子的信息:

for(int i = 1; i <= n; i++)	fa[i] = i;

并查集的查询(路径压缩版):

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

并查集的合并:

int x = find(a), y = find(b);
if (x != y)	fa[x] = y; 

并查集模版完整代码:

#include <iostream>
#include <cstdio>
using namespace std;
int n,m;
int fa[1001000];
int find(int x)
{
	if(x == fa[x])	return fa[x];
	else	return fa[x] = find(fa[x]);
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i++)	fa[i] = i;
	for(int i = 1; i <= m; i++)
	{
		int p,b,c;
		scanf("%d%d%d",&p,&b,&c);
		int x = find(fa[b]);
		int y = find(fa[c]);
		if(p == 1)	fa[x] = y;
		if(p == 2)
		{
			if(x == y)	printf("Y\n");
			else	printf("N\n");
		}
	}
	return 0;
}

训练题目:
[模版]并查集
[模版]最小生成树

有些特殊的题目需要对并查集进行变形,比如合并的时候统计当前连通块的大小,这时候需要对 查询或者合并操作进行对应修改。
银河英雄传说
[USACO04OPEN]Cube Stacking

例二代码:

#include <iostream>
#include <cstdio>
using namespace std;
int p,a,b;
int fa[33000],sum[33000],dist[33000];
char ch;
int find(int x)
{
	if(x == fa[x])  return x;
	int k = find(fa[x]);
	dist[x] += dist[fa[x]];
	return fa[x] = k;
}
void unite(int x, int y)
{
	int X = find(x), Y = find(y);
	fa[X] = Y;
	dist[X] = sum[Y];
	sum[Y] += sum[X];
}
int main()
{
	scanf("%d",&p);
	for(int i = 1; i <= 30000; i++)
		sum[i] = 1, fa[i] = i;
	for(int i = 1; i <= p; i++)
	{
		cin >> ch;
		if(ch == 'M')
		{
			cin >> a >> b;
			unite(a, b);
		}
		if(ch == 'C')
		{
			cin >> a;
			int x = find(a);
			printf("%d\n",dist[a]);
		}
	}
	return 0;
}

还有一些开多倍并查集的题目,来表示朋友或敌人的关系。如果题目要求给定数据是不在同一个集合,则可以令 u u u v + n v+n v+n 联合在一起。
BOI团伙
NOI食物链
NOIP关押罪犯

例二代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <stack>
#include <queue>
#include <ctime>
#include <set>
using namespace std;
const int Maxn = 50000 + 50;
int fa[Maxn << 2];
int read()
{
    int rt = 0, in = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') in = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {rt = rt * 10 + ch - '0'; ch = getchar();}
    return rt * in;
}
int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}
void unite(int x, int y){int X = find(x), Y = find(y); fa[X] = Y;}
int main()
{
    int n = read(), k = read(), ans = 0;
    for (int i = 1; i <= 3*n; ++i)   fa[i] = i;
    for (int i = 1; i <= k; ++i)
    {
        int ins = read(), x = read(), y = read();
        if ( x > n || y > n || (ins == 2 && x == y) )
        {
            ++ans;
            continue;
        }
        if (ins == 1)
            if(find(x+n) == find(y) || find(x+2*n) == find(y) || find(x) == find(y+n) || find(x) == find(y+2*n))
            {
                ++ans;
                continue;
            }
            else    unite(x, y), unite(x+n, y+n), unite(x+2*n, y+2*n);
        else
            if(find(x) == find(y) || find(y) == find(x+n))
            {
                ++ans;
                continue;
            }
            else    unite(x, y+n), unite(x+n, y+2*n), unite(x+2*n, y);
    }
    printf("%d\n",ans);
    system("pause");
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值