Time Limit: 1000MS Memory Limit: 10000K

Total Submissions: 64875 Accepted: 19085

Description

动物王国中有三类动物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

Source

【题解】



哎。网上有牛人写了很完整的题解。

我复制一下。

    Part I  - 权值(relation)的确定。
我们根据题意,森林中有3种动物。A吃B,B吃C,C吃A。
我们还要使用并查集,那么,我们就以动物之间的关系来作为并查集每个节点的
权值。
注意,我们不知道所给的动物(题目说了,输入只给编号)所属的种类。
所以,我们可以用动物之间“相对”的关系来确定一个并查集。
0 - 这个节点与它的父节点是同类
1 - 这个节点被它的父节点吃
2 - 这个节点吃它的父节点。 注意,这个0,1,2所代表的意义不是随便制定的,我们看题目中的要求。
说话的时候,第一个数字(下文中,设为d)指定了后面两种动物的关系:
1 - X与Y同类
2 - X吃Y 我们注意到,当 d = 1的时候,( d - 1 ) = 0,也就是我们制定的意义
当 d = 2的时候,( d - 1 ) = 1,代表Y被X吃,也是我们指定的意义。
所以,这个0,1,2不是随便选的 Part II - 路径压缩,以及节点间关系确定
确定了权值之后,我们要确定有关的操作。
我们把所有的动物全初始化。
struct Animal
{
int num; //该节点(node)的编号
int parent; //该node的父亲
int relation; //该node与父节点的关系,0同类,1被父节点吃,2吃父节点
}; Animal ani[50010];
初始化为
For i = 0 to N do
ani[i].num = i;
ani[i].parent = i;
ani[i].relation = 0 ; //自己和自己是同类
End For (1)路径压缩时的节点算法
我们设A,B,C动物集合如下:(为了以后便于举例)
A = { 1 , 2 , 3 ,4 ,5 }
B = { 6 , 7 , 8 ,9 ,10}
C = { 11, 12, 13,14,15}
假如我们已经有了一个集合,分别有3个元素
SET1 = {1,2},我们规定集合中第一个元素为并查集的“代表”
假如现在有语句:
2 2 6
这是一句真话
2是6的父亲
ani[6].parent = 2;
ani[6].relation = 1;
那么,6和1的关系如何呢?
ani[2].parent = 1;
ani[2].relation = 0;
我们可以发现6与2的关系是 1.
通过穷举我们可以发现
ani[now].parent = ani[ani[now].parent].parent;
ani[now].relation = ( ani[now].relation + ani[now.parent].relation ) % 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
这就是路径压缩的节点算法
(2) 集合间关系的确定
在初始化的时候,我们看到,每个集合都是一个元素,就是他本身。
这时候,每个集合都是自洽的(集合中每个元素都不违反题目的规定)
注意,我们使用并查集的目的就是尽量的把路径压缩,使之高度尽量矮
假设我们已经有一个集合
set1 = {1,2,7,10}
set2 = {11,4,8,13},每个编号所属的物种见上文
set3 = {12,5,4,9}
现在有一句话
2 13 2
这是一句真话,X = 13,Y = 2
我们要把这两个集合合并成一个集合。
直接
int a = findParent(ani[X]);
int b = findParent(ani[Y]);
ani[b].parent = a;
就是把Y所在集合的根节点的父亲设置成X所在集合的根节点。
但是,但是!!!!
Y所在集合的根结点与X所在集合的根节点的关系!!!要怎么确定呢?
我们设X,Y集合都是路径压缩过的,高度只有2层
我们先给出计算的公式
ani[b].relation = ( 3 - ani[Y].relation + ( d - 1 ) + ani[X].relation) % 3;
这个公式,是分三部分,这么推出来的
第一部分,好理解的一部分:
( d - 1 ) :这是X和Y之间的relation,X是Y的父节点时,Y的relation就是这个
3 - ani[Y].relation = 根据Y与根节点的关系,逆推根节点与Y的关系
这部分也是穷举法推出来的,我们举例:
j
子 父相对于子的relation(即假如子是父的父节点,那么父的relation应该是什么,因为父现在是根节点,所以父.relation = 0,我们只能根据父的子节点反推子跟父节点的关系)
0 ( 3 - 0 ) % 3 = 0
1(父吃子) ( 3 - 1 ) % 3 = 2 //父吃子
2(子吃父) ( 3 - 2 ) % 3 = 1 //子吃父,一样的哦亲
——————————————————————————————————————————————————————
我们的过程是这样的:
把ani[Y],先连接到ani[X]上,再把ani[Y]的根节点移动到ani[X]上,最后,把ani[Y]的根节点移动到ani[X]的根节点上,这样算relation的
还记得么,如果我们有一个集合,压缩路径的时候父子关系是这么确定的
ani[爷爷].relation = ( ani[父亲].relation + ani[儿子].relation ) % 3
我们已知道,( d - 1 )就是X与Y的relation了
而 (3 - ani[Y].relation)就是 以Y为根节点时,他的父亲的relation
那么
我们假设把Y接到X上,也就说,现在X是Y的父亲,Y原来的根节点现在是Y的儿子
Y的relation + ani[Y]根节点相对于ani[Y]的relation
( ( d - 1 ) + ( 3 - ani[Y].relation) ) % 3
就是ani[Y]的父亲节点与ani[X]的relation了! 那么,不难得到,ani[Y]的根节点与ani[X]根节点的关系是:
( ( d - 1 ) + ( 3 - ani[Y].relation) + ani[X].relation ) % 3 ->应用了同余定理
注意,这个当所有集合都是初始化状态的时候也适用哦
还是以最开头我们给的三个集合(分别代表三个物种)为例
2 1 6
带入公式
ani[6].relation = ( ( 2 - 1 ) + ( 3 - 0 ) + 0 ) % 3 = 1
也就是,6被1吃
Part III - 算法正确性的证明
首先,两个自洽的集合,合并以后仍然是自洽的
这个不难想吧,数学上有个什么对称性定理跟他很像的。
如果理解不了,就这么想!!
当set1和set2合并之后,set2的根节点得到了自己关于set1根节点的
正确relation值,变成了set1根节点的儿子,那么
set2的所有儿子只要用
( ani[X].relation + ani[Y].relation ) % 3就能得到自己正确的relation值了
所以说,针对不在同一集合的两个元素的话,除非违背了(2)和(3),否则永远是真的
(无论这句话说的是什么,我们都可以根据所给X,Y推出两个子节点之间应有的关系,这个关系一确定,所有儿子的关系都可以确定) 其实所有的不同集合到最后都会被合并成一个集合的。
我们只要在一个集合中找那些假话就可以了。
首先,如何判断
1 X Y是不是假话。//此时 d = 1
if ( X 和 Y 不在同一集合)
Union(x,y,xroot,yroot,d)
else
if x.relation != y.relation ->假话
其次,如何判断
2 X Y是不是假话 //此时d = 2
if ( X 和 Y 不在同一集合)
Union(x,y,xroot,yroot,d)
else
(ani[y].relation + 3 - ani[x].relation ) % 3 != 1 ->假话
这个公式是这么来的:
3 - ani[x].relation得到了根节点关于x的relation
ani[y] + 3 - ani[x].relation得到了y关于x的relation
所以,只要y关于x的relation不是1,就是y不被x吃的话,这句话肯定是假话! (2)路径压缩要特别注意的一点(错在这里,要检讨自己)
路径压缩的时候,记得要
先findParent,再给当前节点的relation赋值。
否则有可能因为当前节点的父节点的relation不正确而导致错的稀里哗啦。
例子:
set1 = {1,2,7,10}
set2 = {3,4,8,11}
set3 = {12,5,14,9}
Union(1,3,1,3,1)
Union(3,12,3,12,2)
1 5 1
算5的relation
如果不先更新parent的relation,算出来应该是
( 3 - 0 + 0 + 1 ) % 3 = 1,5被1吃,显然不对
这里面,+ 0的那个0是指根节点 12 的relation(未更新,这里的0是指12与11的relation)
如果更新完了的话,应该是
( 3 - 0 + 2 + 1 ) % 3 = 0 ,5与1是同一物种,对了
这里面的 2 是更新节点12的relation(12与1的relation)

那里把Y接在X的根节点的下方本来应该是这样的。



但是我们可以换个思路

变成如下的放法。



把那三个用红下划线标记的东西累加起来就是relation(a,b)了

知道relation(a,b)了。

令fa[b] = a;

然后再变回



知道了relation(a,b),我们可以通过lelation(a,b)在路径压缩的时候获取relation(b,y);

然后是判断是否为假话的问题。

我们需要判断任意两个点(a,b)的关系;

因为最后进行完路径压缩每个并查集都只有两层。

要获取relation(a,b);

就查看(relation[a]+3-relation[b])%3;

为0表示相同物种,1表示b吃a,2表示a吃b;

这样理解(高度只有两层)

#include <cstdio>

const int MAXN = 59999;

struct node
{
int fa, relation;
}; int n, k,d,x,y,fade = 0;
node ani[MAXN]; int findfather(int x)
{
if (ani[x].fa == x)
return x;
int olfa = ani[x].fa;
ani[x].fa = findfather(ani[x].fa);
ani[x].relation = (ani[x].relation + ani[olfa].relation) % 3;
return ani[x].fa;
} int main()
{
//freopen("F:\\rush.txt", "r", stdin);
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)
{
ani[i].fa = i;
ani[i].relation = 0;
}
for (int i = 1; i <= k; i++)
{
scanf("%d%d%d", &d, &x, &y);
if ((x > n || y > n) || (d == 2 && x==y))
{
fade++;
continue;
}
int a = findfather(x);
int b = findfather(y);
if (a != b)
{
ani[b].fa = a;
ani[b].relation = (3 - ani[y].relation + d - 1 + ani[x].relation) % 3;
}
else
{
int temp = (ani[y].relation + 3 - ani[x].relation) % 3;
if (d == 1)
{
if (temp != 0)
fade++;
}
else
if (d == 2)
{
if (temp != 1)
fade++;
}
}
}
printf("%d\n", fade);
return 0;
}

【29.42%】【POJ 1182】食物链的更多相关文章

  1. poj 1182 食物链 (带关系的并查集)

      食物链 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 44835 Accepted: 13069 Description 动 ...

  2. poj 1182:食物链(种类并查集,食物链问题)

    食物链 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 44168   Accepted: 12878 Description ...

  3. POJ 1182 食物链

    G - 食物链 Time Limit:1000MS     Memory Limit:10000KB     64bit IO Format:%I64d & %I64u Submit Stat ...

  4. POJ 1182 食物链(种类并查集)

    食物链 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 63592   Accepted: 18670 Description ...

  5. POJ 1182 食物链(经典带权并查集 向量思维模式 很重要)

    传送门: http://poj.org/problem?id=1182 食物链 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: ...

  6. POJ 1182——食物链——————【种类并查集】

    食物链 Time Limit:1000MS     Memory Limit:10000KB     64bit IO Format:%I64d & %I64u Submit Status P ...

  7. POJ 1182 食物链 [并查集 带权并查集 开拓思路]

    传送门 P - 食物链 Time Limit:1000MS     Memory Limit:10000KB     64bit IO Format:%I64d & %I64u Submit  ...

  8. 【原创】poj ----- 1182 食物链 解题报告

    题目地址: http://poj.org/problem?id=1182 题目内容: 食物链 Time Limit: 1000MS   Memory Limit: 10000K Total Submi ...

  9. poj 1182 食物链 (并查集)

    http://poj.org/problem?id=1182 关于并查集 很好的一道题,开始也看了一直没懂.这次是因为<挑战程序设计竞赛>书上有讲解看了几遍终于懂了.是一种很好的思路,跟网 ...

  10. POJ 1182 食物链(并查集拆点)

    [题目链接] http://poj.org/problem?id=1182 [题目大意] 草原上有三种物种,分别为A,B,C A吃B,B吃C,C吃A. 1 x y表示x和y是同类,2 x y表示x吃y ...

随机推荐

  1. 【例题 8-13 UVA - 11093】Just Finish it up

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 尺取法. 假设现在取[l..r]这一段. 然后发现累加的和小于0了. 那么方法只能是不走l..l+1这一段了 即delta递减(p[ ...

  2. 前阿里云CTO章文嵩:怎样做开源才有意义?

    阿里云CTO章文嵩已于昨日离职,据传加盟滴滴.可靠消息透露,章文嵩在滴滴出行担任的是技术高级副总裁的职位.这样一个身价上亿的技术大牛,是怎么看待开源项目的?InfoQ:关于淘宝-阿里系的开源进程,我们 ...

  3. 调用中行接口针对返回报文(xml形式)做相关处理

    最近在对接中行银行接口,在获取返回报文的时候遇到一些问题,现在在这里做个总结 TIP: 在返回报文之前,要对前置机的URL请求,在这期间遇到一个坑,还是通过查看日志才发现问题 在填写转账信息的时候要求 ...

  4. linux 批量设置文件夹755 文件644权限

    linux 批量设置文件夹755 文件644权限 文件来源 http://www.111cn.net/sys/linux/109724.htm 本文章来为各位介绍一篇关于linux 批量设置文件夹75 ...

  5. Codefroces Round#427 div2

    A. Key races time limit per test 1 second memory limit per test 256 megabytes input standard input o ...

  6. iTOP-4412 nfs文件系统启动

    kernel command line type: 普通文件系统(本地)启动:root=/dev/mmcblk0p2 rootfstype=ext4 init=/linuxrc console=tty ...

  7. js面向对象3-继承

    一.了解继承  首先我们一起了解下js中继承,其实继承就是后辈继承前辈的属性和方法. 二.继承的方法 从父类继承属性和方法 这是对象冒充的方法,模仿java的继承方法.实现的原理是,通过改变父类的执行 ...

  8. 洛谷 P1068 分数线划定

    P1068 分数线划定 题目描述 世博会志愿者的选拔工作正在 A 市如火如荼的进行.为了选拔最合适的人才,A 市对 所有报名的选手进行了笔试,笔试分数达到面试分数线的选手方可进入面试.面试分数线根 据 ...

  9. Leetcode:populating_next_right_pointers_in_each_node题解

    一.     题目 对于结构体:struct TreeLinkNode { TreeLinkNode *left; TreeLinkNode *right; TreeLinkNode *next; } ...

  10. 使用IPV6

    使用IPV6 知道IPV6已经很久了,但是一直没有使用过. 我使用的IPV4网络一般是 内网-->外网-->互联网,IPV6也不外乎这样,但是对IPV6而言,必须有它的"世界&q ...