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),输出假话的总数。

题目分析

由于给出X和Y的相对关系,并不能确定X和Y分别是哪种动物,因此没法将动物i属于A、B、C中的哪一类作为一个集合。但是,题目是典型的集合操作,判断两个元素是否符合某种关系,因此仍然考虑使用并查集来解决。
    使用并查集这种结构时候,需要维护相关的数据信息。考虑每两个动物只要能够确定相互关系,则将他们加入一个集合,对于集合中的每个动物,我们维持
该动物和该集合的根节点动物的相对关系。在每次获得两个动物X和Y的相对关系的时候,对于之前已经知道的那些和X或Y相关的动物的关系,只需要知道这些动
物和该共同集合的根节点动物的相对关系,则可以确定他们之间的相对关系。
    在每次得到X和Y之间的关系rel时,先获得他们的根节点,如果根节点p相同,则可以通过X和p的关系,Y和p的关系,确定X和Y在之前就可以确
定的关系rel_1。将rel_1和本次输入的X和Y之间的关系rel进行对比,如果不同,则说明出错;如果根节点不同,分别为px,
py,则将px和py合并,然后根据X和px的关系,X和Y的关系,y和py的关系,得到px和py的关系。

关于带权并查集

带权并查集和普通并查集最大的区别在于带权并查集合并的是可以推算关系的点的集合(可以通过集合中的一个已知值推算这个集合中其他元素的值)。而一般并查集合并的意图在于这个元素属于这个集合。带权并查集相当于把“属于”的定义拓展了一下,拓展为有关系的集合。

实现(c++)

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define MAX_ANIMAL_NUM 50010
int gPar[MAX_ANIMAL_NUM];	//集合的根节点
int gRel[MAX_ANIMAL_NUM];	//节点和所属集合的根节点之间的关系

//gRel[i] == 0, i 和它的最老祖先是同类动物
//gRel[i] == 1, i 吃它的最老祖先
//gRel[i] == 2, i 的最老祖先吃它
void Init(int n){
	for (int i = 1; i <= n; i++){
		gPar[i] = i;
		gRel[i] = 0;
	}
}

//获得集合的根节点,同时维护信息,主要是更新 节点到跟节点的关系数组 gRel[x]
int GetPar(int c){
	if (c != gPar[c]){
		int p = gPar[c];	//c还没被进行路径压缩前,节点c所属集合的根节点 p
		gPar[c] = GetPar(gPar[c]);	//进行路径压缩,路径压缩之后,递归函数返回时,p已经设置了根节点,同时也设置了p和总集合的根节点的关系
		gRel[c] = (gRel[c] + gRel[p]) % 3;	//根据c和p的关系,以及p和总集合的根节点的关系,设置c和总集合的根节点的关系
	}
	return gPar[c];
}

int main(){
	int N, K;
	scanf("%d %d", &N, &K);
	int rel, x, y;
	int error_count = 0;
	Init(N);
	for (int i = 0; i < K; i++){
		scanf("%d %d %d", &rel, &x, &y);
		if (x > N || y > N){
			error_count++;
			continue;
		}
		rel--;
		int p1 = GetPar(x), p2 = GetPar(y);
		if (p1 == p2){
			if ((gRel[x] + 3 - gRel[y]) % 3 != rel){	//关系不一致
				error_count++;
			}
		}
		else{	//对集合进行合并,同时需要注意,根据 x-->p1, x->y, y->p2的关系,得到 p1->p2的关系
			gPar[p1] = p2;
			gRel[p1] = (3 - gRel[x] + rel + gRel[y]) % 3;
		}
	}
	printf("%d\n", error_count);
	return 0;
}
时间: 2024-08-03 10:18:32

poj_1182 并查集的相关文章

CodeForces 745C Hongcow Builds A Nation 并查集

题意: 给了你n个城市 m条边 k个政府 每个政府管辖的区域内不能和其他政府的区域有相连 即政府之间不存在路径 问你在维护这种关系的同时 最多再加多少条边 思路: 先找出来每个联通块 再找出来没有归属的孤立的点 把他们都放到最大的联通块里 然后每个联通块之间的点两两连边是n*(n-1)/2条边 最后算出来的ans-m就好了 (看别人的博客学了一个max_element 1 #include<bits/stdc++.h> 2 #define cl(a,b) memset(a,b,sizeof(a

并查集(个人模版)

并查集: 1 int find(int a) 2 { 3 int r=a; 4 while(f[r]!=r) 5 r=f[r]; 6 int i=a; 7 int j; 8 while(i!=r) 9 { 10 j=f[i]; 11 f[i]=r; 12 i=j; 13 } 14 return r; 15 } 16 int merge(int a,int b) 17 { 18 int A,B; 19 A=find(a); 20 B=find(b); 21 if(A!=B) 22 { 23 f[B

并查集应用

题目描述: One way that the police finds the head of a gang is to check people's phone calls. If there is a phone call between A and B, we say that A and B is related. The weight of a relation is defined to be the total time length of all the phone calls

【bzoj3674】 可持久化并查集加强版

http://www.lydsy.com/JudgeOnline/problem.php?id=3674 (题目链接) 题意 维护并查集3个操作:合并:回到完成第k个操作后的状态:查询. Solution 其实就是用主席树的叶子节点维护并查集的可持久化数组fa[]. 细节 终于认识到了按秩合并的强大,单纯写个路径压缩Re飞,写了路径压缩+按秩合并比单纯的按秩合并每快多少→_→ 代码 // bzoj3674 #include<algorithm> #include<iostream>

BZOJ1015[JSOI2008]星球大战starwar[并查集]

1015: [JSOI2008]星球大战starwar Time Limit: 3 Sec  Memory Limit: 162 MBSubmit: 5253  Solved: 2395[Submit][Status][Discuss] Description 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系.某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球.这些星球通过特殊的以太隧道互相直接或间接地连接. 但好景不长,很快帝国又重

HDU 5606 tree 并查集

tree 把每条边权是1的边断开,发现每个点离他最近的点个数就是他所在的连通块大小. 开一个并查集,每次读到边权是0的边就合并.最后Ans?i??=size[findset(i)],size表示每个并查集根的size Ans_i=size[findset(i)],sizeAns?i??=size[findset(i)],size表示每个并查集根的sizesize. #include<cstdio> #include<cstring> #include<algorithm>

HDU 5441 离线处理 + 并查集

题意:给n个节点m条带权值边的无向图.然后q个问题,每次询问点对的数目,点对需要满足的条件是:1)连通:2)其路径的最大权值不能超过询问值. 分析:如果没次询问一次,dfs一次,很可能超时,因此可以用并查集.离线处理,把边按权值排序,把问题按大小排序.然后离线的过程就是不断向图中加边的过程. 比如样例如下: 然后离线处理,排完序后将会是一条一条的加边:问题也排了序,因此是个累加过程... 1 #include <cstdio> 2 #include <iostream> 3 #in

poj1988 Cube Stacking(并查集

题目地址:http://poj.org/problem?id=1988 题意:共n个数,p个操作.输入p.有两个操作M和C.M x y表示把x所在的栈放到y所在的栈上(比如M 2 6:[2 4]放到[1 6]上为[2 4 1 6]),C x为输出x下面有几个数. 思路:并查集每个集合以栈最下面的数为根,维护两个数组num[x]表示x所在集合节点总数,count[x]表示x下方节点个数.每次查找压缩路径的时候更新count(换父节点的时候每轮都把父节点的count加给儿子,就可以一直更新到x所在栈

习题:过路费(kruskal+并查集+LCA)

过路费  [问题描述]在某个遥远的国家里,有 n 个城市.编号为 1,2,3,…,n.这个国家的政府修 建了 m 条双向道路,每条道路连接着两个城市.政府规定从城市 S 到城市 T 需 要收取的过路费为所经过城市之间道路长度的最大值.如:A 到 B 长度为 2,B 到 C 长度为 3,那么开车从 A 经过 B 到 C 需要上交的过路费为 3. 佳佳是个做生意的人,需要经常开车从任意一个城市到另外一个城市,因此 他需要频繁地上交过路费,由于忙于做生意,所以他无时间来寻找交过路费最低 的行驶路线.然