zoukankan      html  css  js  c++  java
  • 食物链

    食物链

    Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 20000/10000K (Java/Other)
    Total Submission(s) : 2   Accepted Submission(s) : 0
    Problem 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句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。 
    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
     

     思路:种类并查集,并查集的变形,把访问过的点都加入到一个集合中,集合中的点与其根节点之间的关系可以用偏移量表示,delta[x]表示x与根节点之间的关系, delat[x] = 0 表示同类,= 1 表示a吃delta[a],= 2表示被吃。 这样一个集合中的点虽然在一个集合中但关系并不止一种(多种状态),区分他们之间的关系就是通过delta[x]与delta[y]之间的关系!

    以前做过的关于并查集的题目,比如a和b是亲戚,b和c是亲戚,则a和c是亲戚的那种,只是将亲戚放在一个集合中,在一个集合中的点都是亲戚,即一个集合之间的点只有一种关系(一种状态),故容易判断,不需要另设数组记录偏移量。但当一个集合中有多种关系时(状态不止一种)就需要用种类并查集了。

    具体实现如下:

    对于集合里的任意两个元素a,b而言,它们之间必定存在着某种联系,因为并查集中的元素均是有联系的,否则也不会被合并到当前集合中。那么我们就把这2个元素之间的关系量转化为一个偏移量,以食物链的关系而言,不妨假设

    a->b 偏移量0时 a和b同类

    a->b 偏移量1时 a吃b

    a->b 偏移量2时 a被b吃,也就是b吃a

     

    有了这些基础,我们就可以在并查集中完成任意两个元素之间的关系转换了。不妨继续假设,a的当前集合根节点aa,b的当前集合根节点bb,a->b的偏移值为d-1(题中给出的询问已知条件)

    (1)如果aa和bb不相同,那么我们把bb合并到aa上,并且更新delta[bb]值(delta[i]表示i的当前集合根节点到i的偏移量)

    此时 aa->bb = aa->a + a->b + b->bb,可能这一步就是所谓向量思维模式吧

    上式进一步转化为:aa->bb = (3-delta[a]+d-1+delta[b])%3 = delta[bb],(模3是保证偏移量取值始终在[0,2]间)

     

    (2)如果aa和bb相同,那么我们就验证a->b之间的偏移量是否与题中给出的d-1一致

     此时 a->b = a->aa + aa->b = a->aa + bb->b,

    上式进一步转化为:a->b = (3+delta[a]-delta[b])%3,若一致则为真,否则为假。

    以图示表示为:

    一般化总结:

    并查集的偏移向量属于并查集的变形,只要适用于集合数目较少,或是固定的并查集类型。
    比如1182中的食物链只有3个集合,(同类、天敌、食物)。
    2492中的只有两个集合。(同类,异类)。
    1703中的只有两个帮派。(龙帮、蛇帮)。
    在处理此类集合绝对数量少但集合之间关系复杂的并查集,比如(1182,A吃B,B吃C,C吃D,那么A.D是同类。)

    状态存储,只需要在新加一个记录偏移量的数组offset [ MAX ] ,如果只有两个状态,可以直接开bool数据类型。
    在预处理的时候,加上如下
    void makeset ( )
    {
            int i ;
            for ( i = 1 ; i <= n ; ++ i )
            {
                    father [ i ] = i ;
                    offset[ i ] = 0 ;//新加的
            }
    }

    在查集函数中,
    int findset ( int x )
    {
            int t ; 
            if ( father [ x ] == x ) return x ;
            else t = findset( father [ x ] ) ;
            offset [ x ] = ( offset [ x ] + offset [ father [ x ] ] ) % DEPTH ;//DEPTH表示几个状态量
    //如果1182中,DEPTH=3;
            father [ x ] = t ;
            return t ;
    }//使用函数递归调用查找父亲在处理上优于循环。
    在并集函数中,
    void unionset ( int x , int y )
    {
            int fx , fy ;
            fx = findset ( x ) ;
            fy = findset ( y ) ;
            if ( fx == fy ) return ;
            father [ fx ] = fy ;
            offset [ fx ] = ( offset [ y ] - offset [ x ] + d +DEPTH ) % DEPTH ;
    }//d表示x与y的状态偏移量,如1182中,偏移量可能是食物1,或是天敌2;1703中只有反面就是1。
    offset [ x ] = ( offset [ x ] + offset [ father [ x ] ] ) % DEPTH ;
    offset [ fx ] = ( offset [ y ] - offset [ x ] + d +DEPTH ) % DEPTH ;
    在查集函数中,向量转移方程表示儿子与祖先的偏移量=(儿子与父亲的偏移量+父亲和祖先的偏移量)%状态量。(这里的父亲和祖先可能是同一人,但没有关系,因为自己和自己的偏移量为0。)
    在并集函数中,由于要将两个原本不相干的集合合并,d表示在两个集合交汇的地方的最深层的偏移量。
    offset[y]表示y与y祖先的偏移量,offset[x]便是x与x祖先的偏移量。
    加上一个DEPTH是为了防止出现负数,用%会得到意外的结果。

    数据结构:种类并查集

    AC代码:

     1 #include<stdio.h>
     2 int father[50005],depth[50005];
     3 int delta[50005],n;
     4 void init(int n)
     5 {
     6     int i;
     7     for(i = 1;i <= n;i ++)
     8     {
     9         father[i] = i;
    10         depth[i] = 0;
    11         delta[i] = 0;
    12     }
    13     return ;
    14 }
    15 
    16 int find(int x)
    17 {
    18     if(x == father[x])
    19       return x;
    20     int temp = find(father[x]);
    21     delta[x] = (delta[x]+delta[father[x]])%3;
    22     return father[x] = temp;
    23 }
    24 
    25 void unit(int x,int y,int d)
    26 {
    27     x = find(x);
    28     y = find(y);
    29     if(x == y)
    30       return ;
    31     if(depth[x] > depth[y])
    32     {
    33         father[y] = x;
    34         delta[y] = (delta[x]-delta[y]+d+3)%3;
    35     }
    36     else
    37     {
    38         if(depth[x] < depth[y])
    39         {
    40             father[x] = y;
    41             delta[x] = (delta[y]-delta[x]+d+3)%3;
    42         }
    43         else
    44         {
    45             father[x] = y;
    46             delta[x] = (delta[y]-delta[x]+d+3)%3;
    47             depth[y]++;
    48         }
    49     }
    50 }
    51 
    52 int judge(int x,int y,int d)
    53 {
    54     if(x > n || y > n)
    55       return 1;
    56     if(find(x) != find(y))
    57     {
    58         unit(x,y,d);
    59         return 0;
    60     }
    61     if(d-1 == (delta[y]-delta[x]+3)%3)
    62         return 0;
    63     return 1;
    64 }
    65 
    66 int main()
    67 {
    68     int k,cnt;
    69     int d,x,y;
    70     freopen("in.c","r",stdin);
    71     while(~scanf("%d%d",&n,&k))
    72     {
    73         cnt = 0;
    74         init(n);
    75         while(k--)
    76         {
    77             scanf("%d%d%d",&d,&x,&y);
    78             if(judge(x,y,d))
    79               cnt++;
    80         }
    81         printf("%d
    ",cnt);
    82     }
    83     return 0;
    84 }
  • 相关阅读:
    如何在Docker中部署一个可以被外部访问的Apollo配置中心
    ELK+Filebeat日志中心搭建(一)——ElasticSearch7.3.1安装
    零基础配置Hadoop集群——Ambari安装及配置详解
    【转】CSS中position属性( absolute | relative | static | fixed )详解
    浅谈JavaScript中的call和apply
    js 获取某年的某天是第几周
    IE回车的怪异行为
    面向对象的三大特性
    错误175:指定的存储区提供程序在配置中找不到,或者无效 一些感想
    postgresql12.5 windows版安装
  • 原文地址:https://www.cnblogs.com/anhuizhiye/p/3384874.html
Copyright © 2011-2022 走看看