POJ - 1182 食物链 (带权并查集,类似向量的方法。)

题意:

A,B,C三种动物,A吃B, B吃C,C吃A。有n个动物,他们编号为1~n。

输入:第一行n,k,分别表示动物个数,给出k句话(有真有假)。接下来n行每行一句话,每句的格式为三个整数:d,x,y。x,y为动物编号,d为1时表示x,y是同类,d为2时表示x吃y。

说明:假话有三种: 1) 当前的话与前面的某些真的话冲突,就是假话; 2) 当前的话中X或Y比N大,就是假话; 3) 当前的话表示X吃X,就是假话。

输出:假话的个数。

思路:

这个一个带权并查集,

tx 为 x 的父亲节点。

x -> tx   0  x and  tx 同一类

x -> tx  1   x eat tx 

x -> tx  2   tx  eat x

我们开一个 dis 数组 来记录 x 与 tx 的关系。

dis[x] == 0   同一类

dis[x] == 1  x 吃 tx

dis[x] == 2  tx 吃 x

知道这样的我们怎么合并呢。 假如我们用 f[tx] = ty .

则 ,  tx -> x  -> y - > ty  == tx -> ty  这种就类似于向量。一定要注意方向。

所以,,dis[tx]  = -dis[x] + dis[y] + (d - 1);

 

我们判断假话的情况。

x -> y,   x->tx -> y   所以,,(dis[x] - dis[y] + 3) % 3  判断他们的结果是0  还是 1.

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <algorithm>
#define mem(x,v) memset(x,v,sizeof(x)) 
#define go(i,a,b)  for (int i = a; i <= b; i++)
#define og(i,a,b)  for (int i = a; i >= b; i--)
using namespace std;
typedef long long LL;
const double EPS = 1e-10;
const int INF = 0x3f3f3f3f;
const int N = 1e5+10;
int f[N],dis[N];
int find(int k){
	if (f[k] == k) return k;
	int temp = f[k];
	f[k] = find(temp);
	dis[k] = (dis[k] + dis[temp]) %3; //路径压缩。
	return f[k];
}

int main(){
	int n,m;
	int Ans = 0;
	scanf("%d%d",&n,&m);
	go(i,1,n) f[i] = i,dis[i] = 0;
	go(i,1,m){
		int op,x,y;
		scanf("%d%d%d",&op,&x,&y);
		if (x > n || y > n){Ans++; continue;}
		if (op == 2 && x == y) {Ans++; continue;}
		int xx,yy;
		xx = find(x); yy = find(y);
		if (xx != yy){
			f[xx] = yy;
			dis[xx] = (3 + dis[y] - dis[x] + op - 1)%3; //进行合并。
		} else{
			if (op == 1 && dis[x] != dis[y]){ //判断x 和 y 是不是同类。
				Ans++; continue;
			} 
			if (op == 2 && ((dis[x]-dis[y]+3)%3 != 1)){  //判断 x 是不是吃 y
				Ans++;continue;
			}

		}
	}	

	cout<<Ans<<endl;

	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值