【NOI2001】食物链
【NOI2001】食物链
题意
动物王国中有三类动物 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 句话,输出假话的总数。
解法
- 我们开三个并查集,分别表示这个动物 本身 食物 天敌,然后每次合并时,判断一下是否违法,并将对应的关系合并,就可以统计出假话的个数了。
- 我们利用权值并查集,用 $ kind[ i ] $表示 i 与他的父亲的关系。
0 表示他与他的父亲是同类。
1 表示他被他的父亲吃。
2 表示他吃他的父亲。
然后我们要获取父亲与他的关系,就用 $ ( 3 - kind[ i ] ) $就行了。更新关系就用 $ kind[i] = (kind[fa]+kind[i]) \mod 3 $。
代码
第一类
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cctype>
#define INF 2139062143
#define MAX 0x7ffffffffffffff
#define del(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
template<typename T>
inline void read(T&x)
{
x=0;T k=1;char c=getchar();
while(!isdigit(c)){if(c=='-')k=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}x*=k;
}
const int maxn=(5e4+10)*3;
int fa[maxn];
int _find(int x) {
return fa[x]==x ? x : fa[x]=_find(fa[x]);
}
void uni(int x,int y) {
int f1=_find(x),f2=_find(y);
if(f1!=f2) fa[f2]=f1;
}
int n,k;
int main()
{
read(n),read(k);
for(int i=1;i<=3*n;i++) fa[i]=i;// x本身 ,x+n食物 ,x+2*n天敌
int tot=0;
for(int i=1;i<=k;i++) {
int c,x,y;
read(c),read(x),read(y);
if(x>n || y>n) {++tot;continue;}
if(c==1) {
if(_find(x+n)==_find(y) || _find(x+2*n)==_find(y)) ++tot;
else {
uni(x,y),uni(x+n,y+n),uni(x+2*n,y+2*n);
}
}
else {
if(x==y) {++tot;continue;}
if(_find(x)==_find(y) || _find(x+2*n)==_find(y)) ++tot;
else {
uni(x+n,y),uni(x,y+2*n),uni(x+2*n,y+n);
}
}
}
printf("%d",tot);
return 0;
}
第二类
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cctype>
#define INF 2139062143
#define MAX 0x7ffffffffffffff
#define del(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
template<typename T>
inline void read(T&x)
{
x=0;T k=1;char c=getchar();
while(!isdigit(c)){if(c=='-')k=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}x*=k;
}
const int maxn=5e4+10;
int fa[maxn],kind[maxn];
int _find(int x) {
int f=fa[x];
if(f!=x) {
fa[x]=_find(fa[x]);
kind[x]=(kind[x]+kind[f])%3;
}
return fa[x];
}
int n,k;
int main()
{
read(n),read(k);
for(int i=1;i<=n;i++) fa[i]=i;
int tot=0;
for(int i=1;i<=k;i++) {
int c,x,y;
read(c),read(x),read(y);
if(x>n || y>n || (c==2&&x==y)) {++tot;continue;}
if(c==1) {
int f1=_find(x),f2=_find(y);
if(f1==f2) {if(kind[x]!=kind[y]) ++tot;}
else fa[f2]=f1,kind[f2]=(3-kind[y]+kind[x])%3;
}
else {
int f1=_find(x),f2=_find(y);
if(f1==f2) {if((kind[x]-kind[y]+3)%3!=2) ++tot;}
else fa[f2]=f1,kind[f2]=(4-kind[y]+kind[x])%3;
}
}
printf("%d",tot);
return 0;
}