Acwing 240. 食物链
题目描述
动物王国中有三类动物 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句话有的是真的,有的是假的。
当一句话满足下列三条之一时,这句话就是假话,否则就是真话。 - 当前的话与前面的某些真的话冲突,就是假话;
- 当前的话中 X或 Y 比 N大,就是假话;
- 当前的话表示 X吃 X,就是假话。
你的任务是根据给定的 N
和 K 句话,输出假话的总数
输入格式
第一行是两个整数 N 和 K,以一个空格分隔。
以下 K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中 D
表示说法的种类。
若 D=1,则表示 X 和 Y
是同类。若 D=2,则表示 X 吃 Y
输出格式
只有一个整数,表示假话的数目。
数据范围
1≤N≤50000,
0≤K≤100000
思路
给每个点赋值,构造一棵带权树,权重代表距离根的大小,取模,为0代表和根同一类,去1代表吃根,2代表被根吃。
两个操作:
查询根节点,因为要压缩路径我们要维护权值,那么我们要保存到该节点到根节点的距离,然后加上到父节点的距离。然后将该节点的根节点设为父节点。
合并两个集合,因为在遇到一句话叙述,x和y的关系时,有可能两个点不在一个集合。那么我们就需要合并集合。如果x和y时相等关系时,只需将其中一点设置为另外一个点的父节点,然后根两个点之间的距离就是两个
点x和y的距离差,如果x的根节点设为y的根节点,即f[py] = px ;那么d[py] = d[x]-d[y],发过来类似。
如果x吃y,我们设父节点是被子节点吃的,那么类似 [py] = px ;然后d[py] = d[x]-d[y]-1;
判断两句话的真假就是判断两个点的权值是否和题目描述的是否一致。
代码
#include<iostream>
const int N =100010;
using namespace std ;
int cnt = 0, n,k;
int f[N],d[N];
int find(int x)
{
if(f[x] != x)
{
int t = find(f[x]);
d[x] += d[f[x]];
f[x] = t;
}
return f[x];
}
int main()
{
cin>>n>>k;
int s , x , y;
for(int i = 1 ; i <= n ; ++ i) f[i] = i ;
int res = 0;
while(k--)
{
scanf("%d%d%d",&s,&x,&y);
if(x > n || y > n ) res++ ;
else
{
int px = find(x) ,py = find(y);
if(s == 1)
{
if(px != py)
{
f[px] = py;
d[px] = d[y] - d[x];
}
else if((d[x] - d[y] )%3) res ++ ;
}
else
{
if(px != py)
{
f[px] = py;
d[px] = d[y] -1 -d[x];
}
else
{
if( (d[x] + 1 - d[y] )%3) res ++ ;
}
}
}
}
cout<<res;
return 0 ;
}
] + 1 - d[y] )%3) res ++ ;
}
}
}
}
cout<<res;
return 0 ;
}