食物链【扩展域并查集】

>Link

luogu P2024

ybtoj食物链


>Description

动物王国中有三类动物 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 句话有的是真的,有的是假的。
当一句话满足下列三条之一时,这句话就是假话,否则就是真话。

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

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


>解题思路

这里用扩展域并查集(种类并查集)来表示每个动物之间的关系(注意不是种类!)

n n n开3倍,分成3个部分
第一个部分A:1 ~ n,第二个部分B:n ~ 2n,第三个部分C:2n ~ 3n
A吃B,B吃C,C吃A

如果x和y是同类,我们就把A中的x和y、B中的x和y、C中的x和y分别连起来
如果x吃y,我们就把A的x和B中的y、B中的x和C中的y、C中的x和A中的y连起来
每次说一段话,我们就判断x和y的吃与被吃关系,看看与这段话符不符合
如果x和y+n是同一个集合,那说明x吃y……(还有很多种情况)


>代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
using namespace std;

int n, m, ans, fa[3 * N];

int find (int now)
{
    
    
	if (fa[now] == now) return now;
	return fa[now] = find (fa[now]);
}
void connect (int x, int y) //x->y 
{
    
    
	int fx = find (x), fy = find (y);
	fa[fx] = fy;
}

int main()
{
    
    
	scanf ("%d%d", &n, &m);
	for (int i = 1; i <= 3 * n; i++) fa[i] = i;
	int type, x, y;
	while (m--)
	{
    
    
		scanf ("%d%d%d", &type, &x, &y);
		if (x > n || y > n) {
    
    ans++; continue;}
		if (type == 1) //x==y 
		{
    
    
			if (find (x) == find (y + 2 * n))
			{
    
    ans++; continue;}
			if (find (y) == find (x + 2 * n))
			{
    
    ans++; continue;}
			connect (x, y);
			connect (x + n, y + n);
			connect (x + 2 * n, y + 2 * n);
		}
		else //x eat y
		{
    
    
			if (find (x) == find (y))
			{
    
    ans++; continue;}
			if (find (x) == find (y + 2 * n))
			{
    
    ans++; continue;}
			if (find (y) == find (x + n))
			{
    
    ans++; continue;}
			connect (x, y + n);
			connect (x + 2 * n, y);
			connect (x + n, y + 2 * n);
		}
	}
	printf ("%d", ans);
	return 0;
}

Guess you like

Origin blog.csdn.net/qq_43010386/article/details/120947164