poj1182——食物链
动物王国中有三类动物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
食物链,经典的一道带权并查集或者说是种类并查集,感觉全世界都会并查集就我不会了。
真的超难理解
代码:
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=50050;
int p[N];
//num数组 0表示和父节点同类 1表示被父节点吃,2表示吃父节点
//所有取模前提条件都是A吃B B吃C C吃A
int num[N];
int find(int x){
if(x==p[x]){
return x;
}
else{
int fa=p[x];
p[x]=find(p[x]);
//一定要先递归压缩路径,再更新num!
//第一次find的时候num[x]和num[p[x]]都是0
//假如num[x]是1,表示被p[x]吃,而num[p[x]]是2,那么更新后num[x]就是0
//即x吃父节点p[x],而p[x]吃p[p[x]],所以x和p[p[x]]同类,num[x]为0
//同样道理,如果num[x]是2,而num[p[x]]是1,更新后num[x]也为0
//如果num[x]和num[p[x]]同为1或者同为2,根据A吃B B吃C C吃A,取模3即可(巧妙)
//这里一定要先保存这个fa,不能用递归更新后的p[a]
num[x]=(num[x]+num[fa])%3;
return p[x];
}
}
bool join(int a,int b,int q){
int fa = find(a);
int fb = find(b);
//同一个根节点
if(fa == fb){
//q是操作 1表示同类 2表示a吃b
//q如果是1 那么a和b就是同类,那么num值不可能出现一个1一个2的情况
//q如果是2 那么a吃b,那么为了使得满足三角关系,b必须吃父节点,也就是num[b]为2,num[a]为1
//或者num[b]为0,num[a]为2,即a吃b,a吃fa/fb,b和父节点同类
// .....(fa/fb).....
// a---------------------b
if(num[b] != (num[a] + (q - 1)) % 3){
return true;
}
else{
return false;
}
}
else{
//合并
p[fb] = fa;
//q是1的情况,a,b同类,num[fb]为0
//q是2的情况,a吃b,有几种合并的情况
//第一种:a本身是根节点,b有父节点,这样a吃b,合并后其实是p[fb]=a,num[fb]要分情况,
//如果b是被fb吃,那么a和fb就应该是同类,所以num[b]=1 num[a]=0 得到num[fb]=0
//第二种:a有父节点,b本身是根节点,这样a b fa就成了一个三角,a吃b,所以
//如果a被fa吃,那么b一定要吃fa,更新前num[b]是0(本身是根节点),
//num[a]是1 得到num[fb]也就是num[b]为2 符合b吃fa
num[fb] = (3-num[b]+num[a]+(q-1))%3;
//没有关系连通,所以肯定不会矛盾
return false;
}
}
int main(void){
//freopen("data.txt","r",stdin);
int n,k;
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++){
p[i]=i;
}
int q,a,b;
int cnt=0;
while(k--){
scanf("%d%d%d",&q,&a,&b);
if(a>n || b>n ||(q==2 && a==b)){
cnt++;
}
else{
if(join(a,b,q)){
cnt++;
}
}
}
printf("%d\n",cnt);
return 0;
}