- 建模
巧妙的建模,由于题目中只有三种动物,并且这三种动物的食物链构成了一个环,而这种环和取模后循环有着密切的关系。
具体的:
将所有的动物合并到一个并查集里,
现在有两个点 x , y x,y x,y- d [ x ] − d [ y ] ≡ 0 ( m o d 3 ) d[x]- d[y]\equiv 0(mod\ \ 3) d[x]−d[y]≡0(mod 3),说明, x x x和 y y y是同类。
- d [ x ] − d [ y ] ≡ 1 ( m o d 3 ) d[x]- d[y]\equiv 1(mod\ \ 3) d[x]−d[y]≡1(mod 3),说明, x x x吃 y y y。
- d [ x ] − d [ y ] ≡ 2 m o d 3 ) d[x]- d[y]\equiv 2mod\ \ 3) d[x]−d[y]≡2mod 3), 说明, y y y吃 x x x。
所以,在处理两种动物时,先判断它们已经有没有关系,如果有,判断是否发生矛盾;
如果没有,去构建它们之间的关系。
- 边带权的并查集的处理要点
- 路径压缩时,注意同步更新距离。
- 合并永远发生在两个集合的根节点上,所以需要自己计算出二者之间的距离。
#include<stdio.h>
using namespace std;
const int N = 50010;
int f[N], d[N], n, k;
int find(int x){
if(f[x]!=x){
int root = find(f[x]);
d[x] += d[f[x]];
f[x] = root;
}
return f[x];
}
int main(){
scanf("%d%d",&n,&k);
int ans = 0;
for(int i=1;i<=n;i++) f[i] = i;
while(k--){
int t,x,y;
scanf("%d%d%d",&t,&x,&y);
if(x<1 || x>n || y<1 || y>n) ans++;
else{
int px = find(x);
int py = find(y);
if(t==1){
if(px==py){
// 之前的信息确定它们之间是有关系的
if( (d[x]-d[y])%3 ) ans++;
}else{
// 目前还没有关系,即将合并
f[px] = py; //合并永远只发生在根节点上
d[px] = ( (d[y]-d[x])%3+3)%3;
}
}else if(t==2){
if(px==py){
if( ((d[x]-d[y])%3+3)%3 != 1 ) ans++;
}else{
f[px] = py;
d[px] = ((d[y]-d[x]+1)%3+3)%3;
}
}
}
}
printf("%d\n",ans);
return 0;
}