计蒜客习题:找出所有谎言


问题描述

蒜头君有很多卡片,每张卡片正面上印着“剪刀”,“石头”或者“布”三种图案中的一种,反面则印着卡片的序号。“剪刀”,“石头”和“布”三种构成了一个有趣的环形,“剪刀”可以战胜“布”,“布”可以战胜“石头”,“石头”可以战胜“剪刀”。
现有 N 张卡片,以 1-N 编号。每张卡片印着“剪刀”,“石头”,“布”中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这 N 张卡片所构成的关系进行描述:
第一种说法是“1 X Y”,表示 X 号卡片和 Y 号卡片是同一种卡片。
第二种说法是“2 X Y”,表示 X 号卡片可以战胜 Y 号卡片。
蒜头君对 N张卡片,用上述两种说法,一句接一句地说出 K 句话,这K 句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中 X 或 Y 的值比 N 大,就是假话;
3) 当前的话表示 X 能战胜 X ,就是假话。
你的任务是根据给定的 N 和 K 句话,计算假话的总数。
输入格式
第一行是两个整数N(1≤N≤50,000) 和K(0≤K≤100,000),以一个空格分隔。以下 K 行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中 D 表示说法的种类。
若 D=1,则表示 X 和 Y 是同一种卡片。
若 D=2,则表示 X 能战胜 Y。
输出格式
只有一个整数,表示假话的数目。
样例输入
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
样例输出
2
3
4


AC代码

#include
using namespace std;
const int N=5*1e4+7;
const int mod=1000000007;
int pre[N],f[N];
int a,b,c,flag,ans;
int finds(int x){
    if(x!=pre[x]){
        int xx=pre[x];
        pre[x]=finds(pre[x]);
        f[x]=(f[x]+f[xx])%3;
    }
    return pre[x];
}
void merges(int x,int y){
    int xx=finds(x);
    int yy=finds(y);
    if(xx!=yy){
        pre[xx]=yy;
        f[xx]=(f[y]-f[x]+3+c)%3;
    }
    else if((f[x]-f[y]+3)%3!=c) ans++;
}
int main()
{
    int i,j,t,tt=0;
    int n,m;
    scanf("%d%d",&n,&m);
    for(i=0;i<=n+2;i++) pre[i]=i,f[i]=0;
    ans=0;
    while(m--){
        scanf("%d%d%d",&c,&a,&b);
        if(a>n||b>n||(c==2&&a==b))  ans++;
        else c--,merges(a,b);
    }
    printf("%d\n",ans);
}

你可能感兴趣的:(算法竞赛刷题,#,数据结构,计蒜客NOIP习题)