食物链 POJ - 1182 带权并查集

题解

使用带权并查集求解,思路参考至两位大佬的博客 dalao A dalao B
开两个数组,令f[i]表示i的父节点是谁,r[i]表示i与父节点的关系0表示同类,1表示吃他的父节点,2表示被父节点吃
这样设置012的好处是在f[x]=y、f[y]=z关系传递的时候0+0表示x和y、y和z是同类关系则x和z还是同类关系
1+1表示x吃y、y吃z按照题意则x被z吃结果也是2被吃的关系,2+2道理相同
1+2%3表示x吃y、y被z吃则x和z是同类,2+1道理相同

当x和y父节点相同时可以确定x与y的关系为x->fx->y即r[x]-r[y],-r[y]表示反向的关系,最后+3取模防止负数
父节点不同时合并集合,将x的父节点接在y的父节点后面,r[fx]关系为fx->x->y->fy即-r[x]+z+r[y],z表示x与y的关系

AC代码

#include 
#include 
#define fst first
#define sed second
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int MAXN = 5e4 + 10;
int f[MAXN], r[MAXN]; //f[i]表示i的父节点 r[i]表示i与i的父节点的关系0同类1吃父节点2被父节点吃

int find(int x) //查询x的根节点是谁
{
	if (f[x] != x) //父节点不是自身
	{
		int fx = f[x]; //记录原父节点
		f[x] = find(f[x]); //压缩路径
		r[x] = (r[x] + r[fx]) % 3; //关系传递性 x1y1z->x2z x2y2z->x1z x0y0z->x0z x1y2z->x0z
	}
	return f[x]; //返回自身父节点
}
void join(int x, int y, int z) //建立x与y之间是z的关系
{
	int fx = find(x), fy = find(y); //得到xy父节点并且压缩至直连根节点
	if (fx != fy) //不是同一个父节点
	{
		f[fx] = fy; //将fx接在fy上
		r[fx] = (3 - r[x] + z + r[y]) % 3; //传递 fx->x->y->fy
	}
}
int query(int x, int y) //查询x与y的关系 不能确定返回-1
{
	if (find(x) == find(y)) //同一个根节点时才能确定
		return (r[x] + 3 - r[y]) % 3; //传递 x->root->y
	return -1;
}
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		f[i] = i, r[i] = 0; //自身是自身根节点 自身和自身是同类
	int ans = 0;
	for (int i = 0; i < m; i++)
	{
		int t, x, y;
		scanf("%d%d%d", &t, &x, &y);
		if (x > n || y > n)
			ans++;
		else
		{
			t--; //0同类1吃
			int k = query(x, y); //查询xy的关系
			if (k == -1) //未确定关系
				join(x, y, t);
			else if (t != k) //和已有关系不同
				ans++;
		}
	}
	cout << ans << endl;

	return 0;
}

你可能感兴趣的:(_数据结构_,并查集)