zoukankan      html  css  js  c++  java
  • 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。



  • 相关阅读:
    最新屏蔽微信举报方法
    C# WIN 生成机器码
    Quartz.net的快速简单上手使用以及防止IIS回收停止Job的处理
    MVC、Web API 请求接口报错“自定义错误模块不能识别此错误。”解决
    获取微信短链接的官方接口
    Window 通过cmd查看端口占用、相应进程、杀死进程
    微信域名检测、防封,微信跳转技术揭秘(二) -- 微信跳转揭秘
    微信域名检测、防封,微信跳转技术揭秘(一) -- 域名检测原理及防封方案
    各种比较方便给力的小工具
    《Git学习指南》学习笔记(三)
  • 原文地址:https://www.cnblogs.com/zuiaimiusi/p/10762141.html
Copyright © 2011-2022 走看看