Description
动物王国中有三类动物 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 句话,输出假话的总数。
Input&Output
Input
- 第一行两个整数,N,K,表示有 N 个动物,K 句话。
- 第二行开始每行一句话(按照题目要求)
Output
- 一行,一个整数,表示假话的总数。
Solution
- 本题是经典的剩余系下带偏移量的并查集。我们需要表示三种状态:同类,吃,被吃,分别用0,1,2表示,因此在计算偏移量时需要MOD 3。此外,在合并树时,需要计算父节点到新根节点的距离,我们可以根据 (d[x]+d[fx]-d[y])%k=状态 来反解出距离。
- 当两种动物在同一颗树中,而距离不符合输入时,证明时假话,计数器自增。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #define maxn 50005 using namespace std; int p=3,n,k,q,a,b,res,f[maxn],d[maxn]; inline int rd() { int x=0;char c=getchar(); while(c<'0'||c>'9')c=getchar(); while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x; } int find(int x) { if(x==f[x])return x; int root=find(f[x]); d[x]=(d[x]+d[f[x]])%p; return f[x]=root; } int lnk(int x,int y,int dis) { int fx=find(x),fy=find(y); if(fx!=fy){ f[fx]=fy; d[fx]=(dis-d[x]+d[y]+p)%p; return 0; } else{ if((d[x]-d[y]+p)%p!=dis)return -1; } } int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;++i)f[i]=i; for(int i=1;i<=k;++i){ q=rd(); if(q==1){ a=rd();b=rd(); if(a>n||b>n){res++;continue;} int tmp=lnk(a,b,0); if(tmp==-1)res++; } else if(q==2){ a=rd();b=rd(); if(a==b){res++;continue;} if(a>n||b>n){res++;continue;} int tmp=lnk(a,b,1); if(tmp==-1)res++; } } printf("%d",res); return 0; }