CODEVS-1074-食物链-并查集

描述

三种动物 A吃B, B吃C, C吃A.

1, a, b 表示a,b同类

2, a, b 表示a吃b

判断假话个数


分析

  • NOIp前看过, 当时不会, 现在理解起来也有点抽象.
  • 按权值合并的并查集
  • 能确定关系的 x, y 的find到的祖先相同.
  • pa[x] 是 x 的祖先, r[x] 是 x 到祖先的距离, 有三种, 0, 1, 2.  0 表示和祖先同类, 1表示可以吃祖先, 2表示被吃.  r[find(x)] 一定为 0 我用 assert 验证了一下.
  • 查找时先记录下 x 当前的祖先 px, 可知当 x 和 px 最初确定关系的时候 r[px] 一定为0. 而递归 find(px) 之后 r[px] 的数值就是 px 到现在的祖先的距离, x 和 px 现在的祖先是相同的, 那么 x 到现在祖先的距离就是 x 到原来 px 的距离加上 px 到现在祖先的距离.
  • 合并 x, y 时, 先px = find(x), py = find(y). 比如要把 pa[px] 设成 py, 那 py 是不会变了, 考虑 px 的秩怎么变. 如果合并后要求 x, y 同类, 那么 x 到 py 的距离应当等于此时的 r[y], 那么 r[x] + r[px] == r[y], 所以 r[px] = r[y] - r[x]. 这样在路径压缩后新的 r[x] 就等于 r[y] 了.
#include 
   
   
    
    
#include 
    
    
     
     
using namespace std;

const int maxn = 50000 + 10;
int pa[maxn], r[maxn];

int find(int x) {
	if(pa[x] == x) return x;
	int px = pa[x];
	pa[x] = find(px);
	r[x] = (r[px] + r[x]) % 3;
	return pa[x];
}

int main() {
	int n, m, ans = 0;
	scanf("%d %d", &n, &m);
	for(int x = 1; x <= n; x++) pa[x] = x;
	for(int i = 1; i <= m; i++) {
		int k, x, y, px, py;
		scanf("%d %d %d", &k, &x, &y);
		if(x > n || y > n) { ans++; continue; }
		px = find(x); py = find(y);
		assert(r[px] == 0);
		assert(r[py] == 0);
		if(k == 1) {
			if(px == py) { if(r[x] != r[y]) ans++; }
			else pa[px] = py, r[px] = (r[y]-r[x]+3) % 3;
		} else {
			if(px == py) { if(r[x] != (r[y]+1) % 3) ans++; }
			else pa[px] = py, r[px] = (r[y]-r[x]+4) % 3;
		}
	}
	printf("%d\n", ans);
	return 0;
}

    
    
   
   


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值