您的位置:首页 > 其它

poj1182(加权值的并查集)

2015-08-10 14:25 288 查看
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


转载以下博客的文字:

链接入口:/article/4893162.html

觉得这个博客写的挺清晰,解释的挺详细的,自己多加思考就更清晰了。

大体思想:

1、如果两个物种有联系,不管是吃,被吃还是同类,它们之间应该是有一条径路可达的,

也就是它们在一个合集中。

2、如果a,b有关系,b,c有关系,那么a,c之间的关系式可以通过两者的关系推出来的。

OK,下面围绕着上面的两个思想来逐一拆分。

首先就是怎么把有关系的物种放到同一个合集中去,这就要需用到并查集了。每一次入输d,x,y,

也就是相当于x,y之间有一条权为d的径路。先忽略这个权值,直接斟酌路径,那并查集的路径建立就

不用我说了。一个parent数组,parent[i]表现从parent[i]到i有一条径路。OK,那不同的物食圈就构

成了一个连通区域。每个连通区域都有一个根点节。
下面斟酌怎么处理这个权

先说点数学的货色,任何一种偏序关系都足满自反、对称、传递。

自反:自己跟自己满足偏序关系。

对称:a,b的偏序关系为r,则b,a的偏序关系为~r.表现求反。

传递:a,b的偏序关系为r1,b,c的偏序关系为r2,a,c的偏序关系为r1+r2.
为了便利,用一个relation数组来维护这个权值。relation[i]表现的是i在所的连通区域的

根点节到i的关系。先略忽这个关系数组的维护过程,把团体的思绪理清晰。如果有两个物种加进来,

就有两种情况,要么它们在同一个连通集里头。要么不在同一个连通集里头。
一、两者在同一个连通集里头:

1、新加的关系表明x,y是同类,那么它们两个分别到连通区域根点节的关系应该是一样的,

要不就矛盾了。(记为case1)

2、如果新加的关系表明x,y不是同类,那么在当前参加y,x相对根节点的关系和x本来相对根点节的

关系应该是不变的,否则就矛盾了。(记为case2)
二、两者在不同的连通集里头:就直接连接两个连通集就能够了。(记为case3)
路径压缩处理:

由于后来物种会越来越多,我们不希望食物链拉的很长,所以会尽可能的让全部的点节都直接和根节点

相连。这样整个连通的图就有点呈现出星形。
怎么维护关系数组:

数组里头的每个元素的取值要么是0(同类),要么是1(父吃子),要么是2(子吃父)。至于为什么要

这么设置(因为题目中1表示同类,而我们定义0表示同类,相对都应该减一,所以题目中的2表示父吃子在我们这里

应该是2-1=1,,1表示父吃子),参考一另篇博客http://blog.csdn.net/c0de4fun/article/details/7318642,

这里是不能随便定义的。设前面的数据我已经处理好了,现在要处理d,x,y.为了叙说的便利,

记relation[x]为x根->x.那么在现就有三种情况:

case1:(同一个集合且同类)

这种情况x根与y根雷同。如果x根->x与y根->y不同,表明x,y不是同类,与d=1矛盾。

case2:(同一个集合但不同类)

这种情况x根与y根雷同。如果参加y之后,(x根->x) = (x根(即y根)->y + y->x),如果新求出来

的关系与本身已有的x根->x的关系不同,则矛盾。

case3:(在不同集合中)

这种情况x根与y根不同。由于这里添加的是x到y的一条有向边。将y根的父点节设置为x根,更新y根父点

节到x根的关系,即x根->y根=x根->x+x->y+y->y根,由于这里都是有向边,所以更新关系的时候注意关

系的方向。这里需要注意,我们只更新了两个根之间的关系,x根与原来的y所在的连通区域里头的节点

的关系都没有更新,这就是为什么要在一开始判断之前就要调用Find函数,更新每个点节到其根点节的

关系。
初始条件:

有了这个递推,就好办了。初始条件parent就是并查集一般的初始条件,父点节于等自己。由于初始的

时候父节点是自己,当然自己跟自己的关系肯定是同类咯,也就是relation[i]=0

#include <iostream>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int MAXN = 100010;

int f[MAXN],rela[MAXN];

int ff(int x)
{
int tem=f[x];
if(tem!=x)
f[x]=ff(tem),rela[x]=(rela[x]+rela[tem])%3;
return f[x];
}
int main()
{
int n,m,d,x,y;
scanf("%d%d",&n,&m);
for(int i=0;i<=MAXN;i++)
f[i]=i,rela[i]=0;
int s=0;
while(m--)
{
scanf("%d%d%d",&d,&x,&y);
if(x > n || y > n)
{s++;continue;}
if(d == 2 && x == y)
{s++;continue;}
int tem1=ff(x),tem2=ff(y);
if(tem1==tem2)
{
if(d==1&&rela[x]!=rela[y])
s++;
else if(d==2&&rela[x]!=(rela[y] + 2)%3)
s++;
}
else
{
f[tem2]=tem1;
rela[tem2]=(rela[x]+(d-1)+(3-rela[y]))%3;
}
}printf("%d\n",s);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: