POJ 1182 食物链(带权并查集)
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 65579 | Accepted: 19336 |
Description
现有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
以下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
思路
解法一:来自《挑战程序设计竞赛》
由于N和K很大,所以必须高效地维护动物之间的关系,并快速判断是否产生了矛盾。并查集是维护 “属于同一组” 的数据结构,但是在本题中,并不只有属于同一类的信息,还有捕食关系的存在。因此需要开动脑筋维护这些关系。
对于每只动物i创建3个元素i-A, i-B, i-C, 并用这3*N个元素建立并查集。这个并查集维护如下信息:
- i-x 表示 “i属于种类x”。
- 并查集里的每一个组表示组内所有元素代表的情况都同时发生或不发生。
例如,如果i-A和j-B在同一个组里,就表示如果i属于种类A那么j一定属于种类B,如果j属于种类B那么i一定属于种类A。因此,对于每一条信息,只需要按照下面进行操作就可以了。
- 第一种,x和y属于同一种类———合并x-A和y-A、x-B和y-B、x-C和y-C。
- 第二种,x吃y—————————合并x-A和y-B、x-B和y-C、x-C和y-A。
不过在合并之前需要先判断合并是否会产生矛盾。例如在第一种信息的情况下,需要检查比如x-A和y-B或者y-C是否在同一组等信息。
解法二:加权并查集
思路来源于:click here
Part I - 权值(relation)的确定。
注:每个节点对应的 relation[]值记录他与根节点的关系:
我们根据题意,森林中有3种动物。A吃B,B吃C,C吃A。
我们还要使用并查集,那么,我们就以动物之间的关系来作为并查集每个节点的权值。
注意,我们不知道所给的动物(题目说了,输入只给编号)所属的种类。
所以,我们可以用动物之间“相对”的关系来确定一个并查集。
- 0 - 这个节点与它的父节点是同类
- 1 - 这个节点被它的父节点吃
- 2 - 这个节点吃它的父节点
注意,这个0,1,2所代表的意义不是随便制定的,我们看题目中的要求。
输入的时候,第一个数字(下文中,设为d)指定了后面两种动物的关系:
- 1 - X与Y同类
- 2 - X吃Y
我们注意到:
- 如果d == 1 则 x 和 y 是同类 ,那么 y 对 x 的关系是 0
- 如果d == 2 则 x 吃了 y, 那么 y 对 x 的关系是 1, x 对 y 的关系是 2.
- 综上所述 ,无论 d为1 或者是为 2, y 对 x 的关系都是 d-1
所以,这个0,1,2不是随便选的
Part II - 路径压缩,以及节点间关系确定
确定了权值之后,我们要确定有关的操作。
我们把所有的动物全初始化 relation[i]=0,f[i]=i
(1)路径压缩时的节点算法
通过穷举我们可以发现
f[now]=f[f[now]]
relation[now]=(relation[now]+relation[f[now]]) % 3
这个路径压缩算法是正确的
关于这个路径压缩算法,还有一点需要注意的地方,我们一会再谈
注意,根据当前节点的relation和当前节点父节点的relation推出
当前节点与其父节点的父节点的relation这个公式十分重要!!
它推不出来下面都理解不了!!自己用穷举法推一下:
好吧,为了方便伸手党,我给出穷举过程
i j
爷爷 父亲 儿子 儿子与爷爷
0 0 (i + j)%3 = 0
0 1 (i + j)%3 = 1
0 2 (i + j)%3 = 2
1 0 (i + j)%3 = 1
1 1 (i + j)%3 = 2
1 2 (i + j)%3 = 0
2 0 (i + j)%3 = 2
2 1 (i + j)%3 = 0
2 2 (i + j)%3 = 1
这样可以看到,( 儿子relation + 父亲relation ) % 3 = 儿子对爷爷的relation
所以有relation[now]=(relation[now]+relation[f[now]]) % 3
这就是路径压缩的节点算法
(2) 集合间关系的确定
当确定D X Y 的关系的正确性后,要把二者的集合合并为一个集合。就是把Y的集合的根节点其父亲设置为X的集合的根节点。
y的根节点与x的根节点间的关系(即fy与fx的关系):
relation[find(y)]=(3-relation[y]+(d-1)+relation[x]) % 3;
这个公式,是分三部分,这么推出来的:
( d - 1 ) :这是X和Y之间的relation,X是Y的父节点时,Y的relation就是这个
3 - relation[y] = 根据Y与根节点的关系,逆推根节点与Y的关系
这部分也是穷举法推出来的,我们举例:
0(父子同类)( 3 - 0 ) % 3 = 0
1(父吃子) ( 3 - 1 ) % 3 = 2 //父吃子
2(子吃父) ( 3 - 2 ) % 3 = 1 //子吃父
所以有y的父结点与x的父结点关系relation[find(y)]=(3-relation[y]+(d-1)+relation[x]) % 3;
注意,这个当所有集合都是初始化状态的时候也适用
Part III - 判断
每次输入一组数据 d, x, y判断是否超过 N 后,先通过find()函数找他们的根节点从而判断他们是否在同一棵树中。(也就是是否有确定的关系)
1.如果在同一棵树中find(x) == find(y):直接判断是否说谎。
1)如果 d ==1,那么 x 与 y 应该是同类,他们的r[]应该相等,如果不相等,则说谎数 +1
2)如果 d==2,那么 x 应该吃了 y,也就是 (r[x]+1)%3 == r[y]如果不满足,则说谎数 +1
如何判断 x 吃了 y 是 (r[x]+1)%3 == r[y],请看下图:(PS:箭头方向指向被吃方)
2.如果不在同一棵树中:那么合并 x 与 y 分别所在的树。
合并树时要注意顺序,我这里是把 x 树的根当做主根,否则会WA的很惨
再次提醒:找父亲节点时,要不断更新 r[]的值。这里有一个关系:如果 x 和y 为关系 r1, y 和 z 为关系 r2,那么 x 和z的关系就是 (r1+r2)%3,证明过程上面已经论证过了。
#include<stdio.h> #include<string.h> const int maxn = 50005; int N,fa[maxn*3]; int find(int x) { int r = x; while (r != fa[r]) r = fa[r]; int i = x,j; while (i != r) { j = fa[i]; fa[i] = r; i = j; } return r; } void unite(int x,int y) { x = find(x),y = find(y); if (x != y) fa[x] = y; } bool same(int x,int y) { return find(x) == find(y); } int main() { int K,D,X,Y,res = 0; scanf("%d%d",&N,&K); for (int i = 1;i <= 3*N;i++) fa[i] = i; while (K--) { scanf("%d%d%d",&D,&X,&Y); if (X < 1 || X > N || Y < 1 || Y > N) { res++; continue; } if (D == 1) { if (same(X,Y+N) || same(X,Y+2*N)) res++; else unite(X,Y),unite(X+N,Y+N),unite(X+2*N,Y+2*N); } else if (D == 2) { if (same(X,Y) || same(X,Y+2*N)) res++; else unite(X,Y+N),unite(X+N,Y+2*N),unite(X+2*N,Y); } } printf("%d\n",res); return 0; }
#include<stdio.h> const int maxn = 50005; int r[maxn],fa[maxn]; int find(int x) { if (x == fa[x]) return fa[x]; int tmp = fa[x]; fa[x] = find(fa[x]); r[x] = (r[x] + r[tmp])%3; return fa[x]; } void unite(int x,int y,int d) { int fx = find(x),fy = find(y); fa[fy] = fx; //y被x吃,所以以x作为y的根节点 r[fy] = (r[x] - r[y] + 3 + (d - 1))%3; } int main() { int N,K,X,Y,D,res = 0; scanf("%d%d",&N,&K); for (int i = 1;i <= N;i++) r[i] = 0,fa[i] = i; while (K--) { scanf("%d%d%d",&D,&X,&Y); if (X > N || Y > N ||(X == Y && D == 2)) res++; else if (find(X) == find(Y)) { if (D == 1 && r[X] != r[Y]) res++; if (D == 2 && (r[X] + 1) % 3 != r[Y]) res++; } else unite(X,Y,D); } printf("%d\n",res); return 0; }
POJ 1182 食物链(带权并查集)的更多相关文章
- poj 1182 食物链 带权并查集
食物链是并查集的进阶运用的一道非常经典的题目. 题目如下: 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A, ...
- POJ 1182 食物链 (带权并查集 && 向量偏移)
题意 : 中文题就不说题意了…… 分析 : 通过普通并查集的整理归类, 能够单纯地知道某些元素是否在同一个集合内.但是题目不仅只有种类之分, 还有种类之间的关系, 即同类以及吃与被吃, 而且重点是题目 ...
- K - Find them, Catch them POJ - 1703 (带权并查集)
题目链接: K - Find them, Catch them POJ - 1703 题目大意:警方决定捣毁两大犯罪团伙:龙帮和蛇帮,显然一个帮派至少有一人.该城有N个罪犯,编号从1至N(N<= ...
- POJ - 2912 Rochambeau (带权并查集+枚举)
题意:有N个人被分为了三组,其中有一个人是开了挂的.同组的人的关系是‘=’,不同组的人关系是‘<’或'>',但是开了挂的人可以给出自己和他人任意的关系.现在要根据M条关系找出这个开了挂的人 ...
- A Bug's Life POJ - 2492 (带权并查集)
A Bug's Life POJ - 2492 Background Professor Hopper is researching the sexual behavior of a rare spe ...
- poj1182 食物链 带权并查集
题目传送门 题目大意:大家都懂. 思路: 今天给实验室的学弟学妹们讲的带权并查集,本来不想细讲的,但是被学弟学妹们的态度感动了,所以写了一下这个博客,思想在今天白天已经讲过了,所以直接上代码. 首先, ...
- poj 1182:食物链(种类并查集,食物链问题)
食物链 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 44168 Accepted: 12878 Description ...
- POJ 1182 食物链(种类并查集)
食物链 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 63592 Accepted: 18670 Description ...
- poj 1182 食物链(关系并查集)
食物链 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 62824 Accepted: 18432 Description ...
- POJ 1182 食物链 (种类并查集)
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种.有人用两种说 ...
随机推荐
- myeclipse中发送邮件出现Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/mail/util/LineInputStream
出现这个问题的原因是jar包版本不统一,解决方法如下: 我在项目导入了jar包 与myeclipse自带jar冲突了 删除Java EE 5 Libraries/javaee.jar/mail里的包有 ...
- Theano1.1-安装
之前一直想弄theano,可是python不是很懂,在学习了一段时间之后开始安装theano.当然官网上的安装资料是全,可是也太繁琐了.这里介绍的是最简单,最方面的安装theano的方法.官网首页:h ...
- VS Code First使用Mysql数据库详解
最近电脑出毛病了,自己装显卡驱动给装死了开不了机,自己研究了两天也没解决,只有去修电脑的找专业人员,说起来惭愧,虽然自己是搞计算机的可电脑自己重装系统都还搞不定.重装系统又清理灰尘花了50大洋,现在用 ...
- Matlab中的向量
1. 向量的创建 1)直接输入: 行向量:a=[1,2,3,4,5] 列向量:a=[1;2;3;4;5] 2)用“:”生成向量 a=J:K 生成的行向量是a=[J,J+1,…, ...
- JavaScript鼠标拖拽特效及相关问题总结
#div1{width:200px;height:200px;background:red;position:absolute;} #div2{width:200px;height:200px;bac ...
- alarm
AlarmManager的使用机制有的称呼为全局定时器,有的称呼为闹钟.通过对它的使用,它的作用和Timer有点相似.都有两种相似的用法:(1)在指定时长后执行某项操作 (2)周期性的执行某项操作 在 ...
- Git与GitHub到底有什么联系?
Git与GitHub区别 git 是一个软件 版本控制器 分享合并代码, 团队开发 时间机器, 可以获取到任意阶段时间节点开发的代码, 代码找回 git, cvs, bitkeeper, svn 典型 ...
- extjs基础 使用图标字体来美化按钮)
下载 Font Awesome 1.拷贝css 和 fonts 到build同级目录 2.需要在index.html中引入css文件 3.在main.js文件中添加 initComponent : f ...
- [转] java中int,char,string三种类型的相互转换
原文地址:http://blog.csdn.net/lisa0220/article/details/6649707 如何将字串 String 转换成整数 int? int i = Integer.v ...
- 【SPOJ 7258】Lexicographical Substring Search
http://www.spoj.com/problems/SUBLEX/ 好难啊. 建出后缀自动机,然后在后缀自动机的每个状态上记录通过这个状态能走到的不同子串的数量.该状态能走到的所有状态的f值的和 ...