【解题报告】 POJ 1182 食物链 并查集的经典应用+相对位置

题目连接: POJ 1182
题目主要是得考虑找一个元素当这个集合的祖先,然后将其他两两动物之间的身份转变到对祖先的身份即可,这里用到一个向量转换相对身份,其实定义是可以改变的,一旦改变身份转换公式将改变。这道题做了我好长时间啊!!!
这里粘上一个人家的解题报告,给爱学习的你: 关于食物链的详解
// POJ 1182 食物链 并查集
//测试数据(右缩进的为假话):
//5 10
//2 1 3
//1 2 1
//2 4 1
//2 3 4
//2 4 5
// 2 3 2
// 1 4 3
// 1 4 5
//1 1 5
// 1 3 5
//
//假设:
//   2    2                   --相对位置        
// A -> B -> C -> A -> B -> C -> A    
// 这样可以推出矛盾。
//
//定义为:
//         0——同类;
//         1——食物;吃父
//         2——天敌。被吃
//     4
//  ./   `\     //食物链
//5,2,1 -> 3    
//动物编号:2->1->3->5  // father[x] = find(father[x]);          递归调用 压缩路径 
//父节点: 1  3  5  5  // rank[x] = (rank[x] + rank[temp]) % 3  当前的相对的 + 父节点的相对的
//相对的: 0  1  2  0  // return father[x];
//           1  3
//           吃 被
//           3  5
//              吃
	
#include <stdio.h>
#include <string.h>

const int MAXA = 50010;
int father[MAXA],sp[MAXA]; // sp special value 

void init(int n){
	memset(sp,0,sizeof(sp)); 
	for (int i = 1; i <= n; i++) // 初始每个动物都以自己为父节点,所以关系为0同类
		father[i] = i;
}

int find (int x){

	if(x == father[x]) return x;

	int temp = father[x];
	father[x] = find(father[x]); // 递归 更新他爹
	sp[x] = (sp[x] + sp[temp]) % 3;	// 压缩更新他爹
	
	return father[x]; // 只要返回它的爹 就可以了
}

void combination(int a,int b,int k){ // a b 关系为 k (this k is cut by 1) we should comb their root
	int ra = father[a];
	int rb = father[b];
	father[ra] = rb; // make rb is the father of ra (把a的祖先接到b祖先上面)
	sp[ra] = (k + sp[b] - sp[a] + 3) % 3;

}


int main(){
//	freopen("in.txt","r",stdin);
	int N,K;
	int D,X,Y;
	int fsum = 0; // 假话的数量
	scanf("%d%d",&N,&K); // 只有一组测试数据
	init(N);
	for (int i = 1 ; i <= K; i++){
		scanf("%d%d%d",&D,&X,&Y);
		if ( (D==2 && X==Y )||( X>N || Y>N ) ){ // the last two rules
//			printf("%d %d %d\n",D,X,Y);
			fsum++;
		}else{
			int rx = find(X);
			int ry = find(Y);
			if (rx == ry){ // if they are in same set ,judge the relationship
				if(sp[X] != (D - 1 + sp[Y] ) % 3){
//					printf("%d %d %d\n",D,X,Y);
					fsum++;
				}
			}else{ // different set ,(combination)fusion
				combination(X,Y,D-1);
			}
		}
	}
	printf("%d\n",fsum);

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值