食物链 题解

题目描述

动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形。A 吃 B,B 吃 C,C 吃 A。

现有 N 个动物,以 1 - N 编号。每个动物都是 A,B,C 中的一种,但是我们并不知道它到底是哪一种。

有人用两种说法对这 N 个动物所构成的食物链关系进行描述:

  • 第一种说法是 1 X Y,表示 X 和 Y 是同类。
  • 第二种说法是2 X Y,表示 X 吃 Y 。

此人对 N 个动物,用上述两种说法,一句接一句地说出 K 句话,这 K 句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。

  • 当前的话与前面的某些真的话冲突,就是假话
  • 当前的话中 X 或 Y 比 N 大,就是假话
  • 当前的话表示 X 吃 X,就是假话

你的任务是根据给定的 N 和 K 句话,输出假话的总数。

输入格式

第一行两个整数,N,K,表示有 N 个动物,K 句话。

第二行开始每行一句话(按照题目要求,见样例)

输出格式

一行,一个整数,表示假话的总数。

样例输入
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
样例输出
3
分析

这道题,应该都看的出来是并查集吧。。不过我们需要维护三倍的并查集……嗯?又玄学了。

首先定义三个生物群系A,B,C(即三个并查集)。我们可以定义 A 表示中立者,B 表示生产者,C 表示消费者。此时关系明显:A 吃 B,A 被 C 吃。我们也可以定义 B 是中立者,这样 C 就成为了生产者,A 就表示消费者。此时关系:B 吃 C,B 被 A 吃。当然还可以……此时关系:C 吃 A,C 被 B 吃。

所以如果同一个群系中的两个生物属于同一集合,代表他们是同类的。如果不同的群系中的两个生物属于同一集合,则A群系里的生物一定可以吃掉B群系里的,B群系里的吃C群系里的,C群系吃B群系里的

这样就让属于同一个集合但表示意义不同的生物的相互关系区分开来了。

然后,实现就很简单了嘛。

AC代码
#include <cstdio>

const int MAXN = 150005;
// 记得开三倍空间
int fa[MAXN];

// 并查集模板
// 1~n代表A群系,n+1~2*n表示B群系,n*2+1~n*3表示C群系
void Make_Set(int n) {
	for(int i = 0; i <= 3 * n; i++) fa[i] = i;
}

int Find_Set(int s) {
	if(fa[s] != s) return fa[s] = Find_Set(fa[s]);
	else return fa[s];
}

void Union_Set(int s, int e) { 
	int u = Find_Set(s);
	int v = Find_Set(e);
	if(u == v) return ;
	fa[v] = u;
}

int main() {
	int n, k;
	// 有n种动物,k句话
	scanf ("%d %d", &n, &k);
	Make_Set(n);
	int ans = 0;
	for(int i = 1; i <= k; i++) {
		int t, x, y;
		scanf ("%d %d %d", &t, &x, &y);
        if(x > n || y > n) { // 如果x,y根本不是生物?
			ans++; // 假话加一
			continue;
		}
        if(t == 2 && x == y) { // 如果自己吃自己?
			ans++; // 假话加一
			continue;
		}
		if(t == 1) {
			if(Find_Set(y) == Find_Set(x + 2 * n) 
			|| Find_Set(x) == Find_Set(y + 2 * n)) { 
			// 如果生活在A群系里的y吃生活在C群系里的x?
			// 或者生活在A群系里的x吃生活在C群系里的y?
			// 同类是不会互相吃的,所以不满足同类
				ans++; // 假话加一
				continue;
			}            
			Union_Set(x, y); 
			Union_Set(x + n, y + n);            
			Union_Set(x + 2 * n, y + 2 * n);
			// 将三个群系内的x,y放入同一集合
		}
        else {            
			if(Find_Set(x) == Find_Set(y) 
			|| Find_Set(x) == Find_Set(y + 2 * n)) {
			// 如果x,y同类?
			// 或者生活在C群系里的y吃生活在A群系里的x?
			// 冲突
				ans++; // 假话加一
				continue;
			}            
			Union_Set(x + 2 * n, y); // C群系吃A群系          
			Union_Set(x + n, y + 2 * n); // B群系吃C群系        
			Union_Set(x, y + n); // A群系吃B群系        
		}  
	}
	printf("%d
", ans); 
	return 0;
}
原文地址:https://www.cnblogs.com/Chain-Forward-Star/p/13868254.html