P2024 题解
题目传送门
省流
一句话概括:在带权并查集模板的基础上多做了个对权值取模
思路
首先考虑怎么样的动物是一类。如图是一条食物链,如
然后可以想到将边权全部赋值为
然而这道题并不能用图直接进行存储,因为图不相连且输入信息会有假话。考虑使用并查集,主要难点在于查找与合并上(用
- 初始化:并查集照常,
D_i 都初始化为0 ,代表自己和自己是同一种动物。 - 查找:在原先的基础上要更新点
x 的权值。则令y 为x 的父亲,更新D_x=(D_x+D_y)\bmod3 。即从x 点到y 点需要将y 的权转移到x 点上。 - 合并:合并注意到有是同类和吃的两种关系,则需要定义传参
w 代表这种关系,可以用0 代表同类,1 代表吃。想要将x 拼接到y 上,需要更新x 的权值。令z 为x 的父亲,则更新D_z=(D_y-D_x+w+3)\bmod3 。其中+3 是为了防止出现负数。
来到主函数的处理。
- 首先处理
X 或Y 比N 大的情况,因为若成立,这一定是一句假话。 - 然后判断若
X 和Y 的父亲不同,按着参数w 合并X 和Y 。 - 只剩下
X 和Y 的父亲相同的情况,此时若仍存在D_X\ne D_Y 且X 和Y 为同类的说法,或它们不能互相吃(D_X\ne(D_Y+1)\bmod3 )两者是吃的关系,这也是一句假话。 - 剩下的情况相当于说了句废话。
最后输出假话的次数即可。
AC CODE
#include<bits/stdc++.h>
using namespace std;
int read(){int x=0;char f=1,ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
const int N=5e4+10;
int fa[N],d[N];
void _init(){
for(int i=0;i<N;++i)
fa[i]=i;
return;
}
int _find(int x){
if(x!=fa[x]){
int temp=fa[x];
fa[x]=_find(fa[x]);
d[x]=(d[x]+d[temp])%3;
}
return fa[x];
}
void _merge(int x,int y,int w){
int xx=_find(x),yy=_find(y);
if(xx!=yy){
fa[xx]=yy;
d[xx]=(w+d[y]-d[x]+3)%3;
}
return;
}
int main(){
_init();
int n=read(),k=read(),cnt=0;
while(k--){
int op=read(),x=read(),y=read();
int xx=_find(x),yy=_find(y);
if(x>n||y>n||op==2&&x==y)
++cnt;
else if(xx!=yy)
_merge(x,y,op-1);
else if(op==1&&d[x]!=d[y]||op==2&&d[x]!=(d[y]+1)%3)
++cnt;
}
printf("%d\n",cnt);
return 0;
}