并查集删点总结

并查集删点操作需要借助虚点,实际上也就是一个映射罢了。

通过index[i]表示i点经过映射后的编号,规定访问一个节点时只能通过它的编号index[i]来访问,初始时将各点的index[i]置为i,表示还未进行删点操作时都映射到本身,在删除点i时,将点i映射到一个虚节点上,即index[i] = m(m为虚节点编号),同时father[index[i]] = index[i] (表示删除点i后i点独立),之后所有涉及到点i的操作都用index[i]来代替,即m。

这样的好处在于find()函数不需要修改,且一定能够保证被删点所在连通块依然连通。

例如下图中,删除了2号点,实际上并没有真的删除它,而是把2号点映射到了5号点,之后每次使用2号点都用5号点来代替,而原来的2号节点不可能再被访问到,变成了垃圾节点。

以​Junk-Mail Filter HDU - 2473为例,放一个模板:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring> 
using namespace std;
//为了删点(例如i点)后不破坏连通性,fa[i]不能改变,因为它是一座桥梁 
//index[i]表示将i映射到index[i],index[i]是虚节点下标,每次merge和find都要先把i通过index映射一下 
//fa数组多开了一倍,为可能出现的虚节点开辟空间
//这样的好处是merge和find函数都不用改变,只是调用的时候传映射过的值就可以了 
int n, m, num;
int fa[1100005], index[100005];
bool vis[1100005];

void init()
{
	for(int i = 0; i <= n-1; i++)
		fa[i] = index[i] = i;//需要对index也初始化,表示一开始都映射到本身 
	num = 0;//表示开了几个虚节点 
 } 

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

void merge(int x, int y)
{
	int fx = find(x), fy = find(y);
	if(fx != fy)
		fa[fx] = fy;
}

void del(int x)//本质上是改变i点的映射 
{
	fa[n+num] = n+num;//自己独立出来 
	index[x] = n+num;
	num++;
}

signed main()
{
	int t = 0;
	while(~scanf("%d%d", &n ,&m))
	{
		t++;
		if(n == 0 && m == 0)
			break;
		init();
		char op[2];
		int a, b;
		for(int i = 1; i <= m; i++)
		{
			scanf("%s", op);
			if(op[0] == 'M')
			{
				scanf("%d%d", &a, &b);
				merge(index[a], index[b]);
			}
			else
			{
				scanf("%d", &a);
				del(a);//这里不可以再取index了,因为index是对原始的n个数映射 
			}
		}
		int ans = 0;
		memset(vis, false, sizeof vis);
		for(int i = 0; i <= n-1; i++)//只能这么求,统计有多少个不同的根节点 
		{
			if(!vis[find(index[i])])
				ans++, vis[find(index[i])] = true;
		}
//求连通块数,假如只看fa[x]==x,1->2->3,分别删1,2,3,会有问题 
//		for(int i = 0; i <= n-1; i++)//也不能这样求,比如1->2,删根节点2,会有错误 
//			if(fa[index[i]] == index[i])
//				ans++; 
		printf("Case #%d: %d\n", t, ans);
	}
	 
    return 0;
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值