POJ1182

https://vjudge.net/problem/POJ-1182

动物王国中有三类动物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句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。

Input

第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。

Output

只有一个整数,表示假话的数目。

Sample Input

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

Sample Output

3
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define maxn 50010
using namespace std;
struct node
{
    int pre;
    int relation;
}p[maxn];
int Find(int x)
{
    int temp;
    if(x==p[x].pre)
        return x;
    temp=p[x].pre;
    p[x].pre=Find(temp);
    p[x].relation=(p[x].relation+p[temp].relation)%3;
    return p[x].pre;
}
int main()
{
    int n,k;
    int ope,a,b;
    int root1,root2;
    int sum=0;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        p[i].pre=i;
        p[i].relation=0;
    }
    for(int i=1;i<=k;i++)
    {
        scanf("%d%d%d",&ope,&a,&b);
        if(a>n||b>n)
        {
            sum++;
            continue;
        }
        if(ope==2&&a==b)
        {
            sum++;
            continue;
        }
        root1=Find(a);
        root2=Find(b);
        if(root1!=root2)
        {
            p[root2].pre=root1;
            p[root2].relation=(p[a].relation+(ope-1)+3-p[b].relation)%3;
        }
        else
        {
            if(ope==1&&p[a].relation!=p[b].relation)
            {
                sum++;
                continue;
            }
            if(ope==2&&(3-p[a].relation+p[b].relation)%3!=ope-1)
            {
                sum++;
                continue;
            }
        }
    }
    cout<<sum<<endl;
    return 0;
}

思路:

//这道题花了我一天才想通,菜是原罪(ಥ _ ಥ)!

1.题中:第一种说法是"1 X Y";第二种说法是"2 X Y"。可以假设变量ope,当的值为 1,表示X和Y是同类;当值为2,表示X吃Y。

题中给了关于X和Y的两种状态,而实际情况中有三种状态需要表示:假设操作变量m,当m值为  0 代表X,Y是同类,1 代表X吃Y,2 代表X被Y吃。

(这里假设X为根节点,m为根节点X到节点Y的操作,特别注意操作的方向,可以把两点的操作关系理解为向量)

那么怎么用假设表示题中给的初始量?我们引入偏移量1,将题中的量减1,可以得到假设的量。即m=ope-1;

2.构建结构体,包含前驱节点和   relation(根节点到该节点的关系)相当于我们上面说的m;

 3.Find()不仅要找根节点(在设节点初值的时候,每个节点都是指向自己的。不断找父节点直到找到一个节点它指向自己,说明它就是根节点。),还要更新操作域的等式p[x].relation=(p[x].relation+p[temp].relation)%3;。

举个栗子来形象化理解一下,(假设树长这样W->X->Y->Z),在这里Find()先查找根节点的时候,(根据前面的假设是父亲节点指向子节点),即找到Z,

Z.relation=(Z.relation+Z.pre.relation)%3=0;     Z->Z

再根据递归 Y.relation=(Y.relation+Z.relation)%3;    Z->Y

再根据递归 X.relation=(X.relation+Y.relation)%3;    Z->X(Z->Y->X) 此时Y的relation已更新

再根据递归W.relation=(W.relation+X.relation)%3;   W.relation代表X->W,初始X.relation代表Y->X,此时已更新代表Z->X;

可将此类比为向量,Z->W=(Z->X+X->W)%3; 

(Z->X+X->W)可以大于等于3,为了保证0<=relation<=2,所以要将(Z->X+X->W)的值 %3。


4.判断假话

2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。

//2)3)的判断比较简单,用if条件判断即可。

1) 当前的话与前面的某些真的话冲突,就是假话;

//如果X,Y非同根,将它们合并为同根。根据一开始分析的假设,我们将X的根节点作为Y根节点的父节点。

操作域怎么更新?

          p[root2].relation=(p[x].relation+(ope-1)+3-p[y].relation)%3;//root2代表Y的根节点,即X,root1代表X的根节点;m=ope-1;

p[x].relation :root1->X

p[y].relation :root2->Y     3-p[y].relation :Y->root2//相对位置发生改变,此时是以Y为根节点来看;

ope-1 :X->Y

root1->root2=(root1->X+X->Y+Y->root2)%3; 

//如果X,Y同根,

ope==1,X,Y同类,如果X与Y的操作数不同则为假话

ope==2,X吃Y,需要验证X->Y是否与ope-1相同

X->Y=X->root1+root1->Y= X->root1+root2->Y(因为X,Y同根)=(3-p[x].relation)+p[y].relation。



原文地址:https://www.cnblogs.com/zuiaimiusi/p/10762141.html