CSU-ACM2018寒假训练9B-并查集&Kruskal D - 经典带权并查集

题目传送门


带权并查集


关系判断数组:

0 : 本节点与父节点是同类

1 : 本节点吃父节点

2 : 本节点被父节点吃


注:题目所给的两种关系序号需预处理减掉1。

即,x与y同类:1-1=0。x吃y:2-1=1


状态压缩时的关系转移方程:

int tmp = f[x]; // important
f[x] = find (f[x]);
rlt[x] = (rlt[x] + rlt[tmp]) % 3;
注:要在更新父节点后才能更新关系

两棵树合并:

void Union (int c,int a,int b)
{
	int A = find (a);
	int B = find (b);
	
	if (A != B)
	{
		f[A] = B;
		rlt[A] = (rlt[b] - rlt[a] + c + 6) % 3;
	}
}
注:合并a,b两不在同一棵树上的节点时,

是将a的祖先连到b节点上。


代码:

#include 
#include 
#include 
#include 
#include 
#define N 55555

using namespace std;

int n,f[N];
int rlt[N];

int find (int x) 
{
	if (f[x] == x)
		return x;
	
	int tmp = f[x];   // important !
	f[x] = find (f[x]);
	
	rlt[x] = (rlt[x] + rlt[tmp]) % 3;
	
	return f[x];
}

void Union (int c,int a,int b)
{
	int A = find (a);
	int B = find (b);
	
	if (A != B)
	{
		f[A] = B;
		rlt[A] = (rlt[b] - rlt[a] + c + 6) % 3;
	}
}

bool check (int c,int a,int b)
{
	if ((a == b && c == 1) || a > n || b > n)
		return false;
	
	int A = find(a);
	int B = find(b);
	
	if (A != B)
		return true;
	
	return (rlt[a] - rlt[b] + 6)% 3 == c;
}

int main ()
{
	int m,a,b,c;
	scanf ("%d%d",&n,&m);
	
	for (int i=0;i<=n;i++)
	{
		f[i] = i;
		rlt[i] = 0;
	}
	
	int cnt = 0;
	for (int i=1;i<=m;i++)
	{
		scanf ("%d%d%d",&c,&a,&b);
		c--;
		if (check(c,a,b))
			Union (c,a,b);
		else
			cnt++;
	}
	
	printf ("%d\n",cnt);
	return 0;
}


带权并查集还很多没理解的地方,

先挖个坑,

记得起来就来填。

你可能感兴趣的:(csuacm2018,并查集,带权并查集)