洛谷 P2024 食物链

来源:互联网 发布:朱棣 知乎 编辑:程序博客网 时间:2024/05/22 12:19

题目描述

动物王国中有三类动物 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 句话,输出假话的总数。

输入输出格式

输入格式:

从 eat.in 中输入数据
第一行两个整数,N,K,表示有 N 个动物,K 句话。
第二行开始每行一句话(按照题目要求,见样例)

输出格式:

输出到 eat.out 中
一行,一个整数,表示假话的总数。

输入输出样例

输入样例#1:

100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5

输出样例#1:

3

说明

1 ≤ N ≤ 5 ∗ 10^4
1 ≤ K ≤ 10^5

分析

1.p[x]表示x根结点。r[x]表示p[x]与x关系。r[x]=0 表示p[x]与x同类;1表示p[x]吃x;2表示x吃p[x]。
2.怎样划分一个集合呢?
注意,这里不是根据x与p[x]是否是同类来划分。而是根据“x与p[x]能否确定两者之间关系”来划分,若能确定x与p[x]关系,则它们同属一个集合
3.怎样判断一句话是不是假话?
假设已读入D ,X ,Y ,先利用find()函数得到X,Y所在集合代表元素fx,fy,若它们在同一集合(即fx==fy)则可以判断这句话真伪:
若 D==1 而 r[X]!=r[Y] 则此话为假.(D==1 表示X与Y为同类,而从r[X]!=r[Y]可以推出 X 与 Y 不同类.矛盾.)
若 D==2 而 r[X]==r[Y](X与Y为同类)或者r[X]==(r[Y]+1)%3(Y吃X)则此话为假。
4.上个问题中r[X]==(r[Y]+1)%3这个式子怎样推来?
假设有Y吃X,那么r[X]和r[Y]值是怎样?
我们来列举一下:

           r[X]=0&&r[Y]=2                    r[X]=1&&r[Y]=0                   r[X]=2&&r[Y]=1

稍微观察一下就知道r[X]=(r[Y]+1)%3;
事实上,对于上个问题有更一般判断方法:
若(r[Y]-r[X]+3)%3!=D-1 ,则此话为假.
5.其他注意事项:
在unionn(d,x,y)过程中若将S(fy)合并到S(fx)上,则相应r[fy]必须更新为fy相对于fx关系。怎样得到更新关系式?
r[fy]=(r[x]-r[y]+d+3)%3;

代码

#include <bits/stdc++.h>#define N 50001using namespace std;int p[N],r[N];int n,k,ans,x,y,d;int find(int x){    if (x!=p[x])    {        int fx=find(p[x]);        r[x]=(r[x]+r[p[x]])%3;        p[x]=fx;    }    return p[x];}bool unionn(int d,int x,int y){    int fx=find(x),fy=find(y);    if (fx==fy)        if ((r[y]-r[x]+3)%3!=d) return 1;        else return 0;    p[fy]=fx;    r[fy]=(r[x]-r[y]+d+3)%3;    return 0;}main(){    scanf("%d %d",&n,&k);    for (int i=1;i<=n;i++)         p[i]=i,r[i]=0;    for (int i=1;i<=k;i++)    {        scanf("%d %d %d",&d,&x,&y);        if(x>n||y>n||(x==y && d==2))        {            ans++;            continue;        }        if (unionn(d-1,x,y)) ans++;    }    printf("%d",ans);}
0 0