食物链 POJ - 1182(并查集)

食物链 POJ - 1182

动物王国中有三类动物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比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。
Input
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
Output
只有一个整数,表示假话的数目。
Sample Input
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
Sample Output
3

题解:
并查集的经典运用。
定义结构体,通过并查集来判断当前的话是否正确。

struct node {
	int root;
	int relation;//0 代表同类 1 代表root吃 2 代表吃root
}animal[maxn];

查找:
在查找时因为节点不仅有父亲节点域,而且还有表示节点与其父亲节点的关系域,查找过程中对父亲节点域的处理和简单的并查集处理一样,即在查找过程中同时实现路径压缩,但正是由于路径压缩,使得表示节点与其父亲节点的关系域发生了变化,所以在路径压缩过程中节点和其对应的父节点的关系域发生了变化(因为路径压缩之前节点x的父亲节点为rootx的话,那么在路径压缩之后节点x的父亲节点就变为了节点rootx的父亲节点rootxx,所以此时animal[x].relation存储的应该是节点x与现在父亲节点rootxx的关系)
在这里插入图片描述
根据图可以推出状态更新公式:
animal[x].relation = (animal[rootx].relation + animal[x].relation) % 3;
合并:
在将元素x与y所在的集合合并时,假设元素x所在的集合编号为rootx,元素y所在的集合编号为rooty,合并时直接将集合rooty挂到集合rootx上,即animal[rooty].root=rootx,此时原来集合rooty中的根节点rooty的关系域也应随之发生变化,因为合并之前rooty的父亲节点就是其自身,故此时animal[rooty].relation=0,而合并之后rooty的父亲节点为rootx,所以此时需判断rootx与rooty的关系,即更新animal[rooty]的值
在这里插入图片描述
此时假设假设animal[x].relation=0(即x与rootx的关系是同类),animal[y].relation=1(即rooty吃y),则有:
1. 输入d=1时,即x和y是同类,则有上述关系可以推出rooty吃rootx,即animal[rooty].relation=2;
2. 输入d=2时,即x吃y,则有上述关系可以推出rooty与rootx是同类(因为rooty吃y,x吃y,则rooty与x是同类,又rootx与x是同类),即animal[rooty].relation=0;
当然,这只是一种可能,其它的可能情况和上面一样分析。

当元素x与元素y在同一集合时,则不需要合并,因为此时x与y的父亲节点相同,可以分情况讨论:
1. d=1时,即x与y是同类时,此时要满足这要求,则必须满足animal[x].relation=animal[y].relation,这很容易推出来.
2. d=2时,即表示x吃y,此时要满足这要求,则也必须满足一定的条件,如x和root是同类(即animal[x].relation=0),此时要满足x吃y,则必须满足root吃y,即animal[y].relation=1,可以像上面一样画图来帮助理解.
rootx!=rooty
推导公式:

animal[rooty].relation = (animal[x].relation + d-1 + 3 - animal[y].relation) % 3;

rootx==rooty
判断x和y的关系是否和输入关系是否一致
推导公式:

  if (d == 1 && animal[x].relation != animal[y].relation) {//非同类关系
		sum++;
		return;
	}
	if (d == 2 && (3 - animal[x].relation + animal[y].relation) % 3 != d - 1) {//非捕食关系
		sum++;
		return;
	}

在这里插入图片描述
大牛思路

AC代码:

PS:cin会T,建议用scanf

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cctype>
#include<iomanip>
#include<map>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<set>
#include<cctype>
#include<string>
#include<stdexcept>
#include<fstream>
#define mem(a,b) memset(a,b,sizeof(a))
#define debug() puts("what the fuck!")
#define debug(a) cout<<#a<<"="<<a<<endl;
#define speed {ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); };
#define ll long long
#define mod 998244353
using namespace std;
const double PI = acos(-1.0);
const int maxn = 1e5 + 10;
const int INF = 0x3f3f3f3f;
const double esp_0 = 1e-6;
ll gcd(ll x, ll y) {
	return y ? gcd(y, x % y) : x;
}
ll lcm(ll x, ll y) {
	return x * y / gcd(x, y);
}
ll extends_gcd(ll a, ll b, ll& x, ll& y) {
	if (b == 0) {
		x = 1;
		y = 0;
		return a;
	}
	ll gcdd=extends_gcd(b, a % b, x, y);
	ll temp = x;
	x = y;
	y = temp - (a / b) * y;
	return gcdd;
}
struct node {
	int root;
	int relation;//0 代表同类 1 代表root吃 2 代表吃root
}animal[maxn];
int n, k;
int d, x, y;
int sum = 0;
int find(int x) {
	int temp;
	if (x == animal[x].root) {
		return x;
	}
	temp = animal[x].root;
	animal[x].root = find(temp);
	animal[x].relation = (animal[temp].relation + animal[x].relation) % 3;//更新relation值
	return animal[x].root;
}
void unite(int x, int y) {
	int fx = find(x);
	int fy = find(y);
	if (fx != fy) {//祖先不相同
		animal[fy].root = fx;
		animal[fy].relation = (animal[x].relation + d - 1 + 3 - animal[y].relation) % 3;
	}
	else {
		if (d == 1 && animal[x].relation != animal[y].relation) {//非同类关系
			sum++;
			return;
		}
		if (d == 2 && (3 - animal[x].relation + animal[y].relation) % 3 != d - 1) {//非捕食关系
			sum++;
			return;
		}
	}
}
void init() {//初始化节点值,i的root是i,所以relation值为0 同类
	for (int i = 1; i <= n; ++i) {
		animal[i].root = i;
		animal[i].relation = 0;
	}
}
int main(){
	scanf("%d%d", &n, &k);
	init();
	for (int i = 0; i < k; ++i) {
		scanf("%d%d%d", &d, &x, &y);
		if (x > n || y > n) {
			sum++;
			continue;
		}
		if (d == 2 && x == y) {
			sum++;
			continue;
		}
		unite(x, y);
	}
	printf("%d\n", sum);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值