• 【poj 1182】食物链(图论--带权并查集)


    题意:有3种动物A、B、C,形成一个“A吃B, B吃C,C吃A ”的食物链。有一个人对N只这3类的动物有M种说法:第一种说法是"1 X Y",表示X和Y是同类。;第二种说法是"2 X Y",表示X吃Y。假设输入为(d,X,Y)。可知,当一句话满足下列三条之一时,这句话就是假话,否则就是真话。 
    1) 当前的话与前面的某些真的话冲突,就是假话; 
    2) 当前的话中X或Y比N大,就是假话; 
    3) 当前的话表示X吃X,就是假话。
    要求输出假话的总数。

    解法:带权并查集。同【bzoj 1202】[HNOI2005] 狡猾的商人(图论--带权并查集+前缀和)类似,判断假话的条件一就是要与之前得到的结果冲突。所以推断出用并查集的原因绝对不是因为有3“种”动物,而是因为这个“之前有结果就判断,没有结果就存储”的操作。

    于是由于已经知道要用并查集,就需要推出树上结点的关系,使f[x]表示动物x与其所在联盟的根节点R的关系(x→R)。再通过枚举的方法,列举出所有情况,得出规律。再应用到具体的函数等操作中。

    QUESTION!!!(这里理应大写加粗~)  OMG!!!∑(゚Д゚ノ)ノ我突然间发现若用“方块图”,我下面的所有推导都显得无比无比的复杂啊!直接转换x→y(定义x下y上)的关系为:x到y的距离 或 x上面方块的个数,所有的都可以一步写出来呀!有神牛告诉我带权并查集都是可以这样理解的吗?!!!!所有的找父亲结点并更新自己的find(x)函数那里,f[x]都是f[x]+f[fx]吗。。。

    推导过程如下:如图1所示,设x,y分别是R的子结点;如图2所示,x→R表示x吃R,相应数字。0为x,R同类,1为x吃R,2为R吃x;具体对应情况枚举如图3所示。
    于是据图3找规律,相加不行便相减,发现(第一行数-第二行数+3)%3=第三行数,即(f[x]-f[y]
    +3)%3=d-1。(d为输入中x,y的关系种类)

    因此,根据这一条树上的规律,我们就可以把它应用到所有函数中了。具体请见代码——

     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cstring>
     4 #include<iostream>
     5 using namespace std;
     6 
     7 const int N=50010,M=100010;
     8 int fa[N],f[N];
     9 int n,m;
    10 
    11 int ffind(int x)
    12 {
    13     if (fa[x]!=x)
    14     {
    15       int fx=fa[x];
    16       fa[x]=ffind(fx);
    17       /*推导:
    18       f[x]=(f[x]-f[fa[x]]+3)%3;
    19           =(f[x]-((3-f[fx])%3)+3)%3;
    20       */
    21       f[x]=(f[x]+f[fx])%3;
    22     }
    23     return fa[x];
    24 }
    25 int main()
    26 {
    27     scanf("%d%d",&n,&m);
    28     int cnt=0;
    29     for (int i=1;i<=n;i++) fa[i]=i,f[i]=0;
    30     for (int i=1;i<=m;i++)
    31     {
    32       int d,x,y;
    33       scanf("%d%d%d",&d,&x,&y);
    34       if (x>n||y>n||(d==2&&x==y)) {cnt++;continue;}
    35       d--;//
    36       int fx=ffind(x),fy=ffind(y);
    37       if (fx!=fy)
    38       {
    39         fa[fx]=fy;
    40         /*推导:
    41         int t=(3-f[x])%3;//fx->x
    42         int tt=(3-d)%3;//y->x
    43         int w=(t-tt+3)%3;//fx->y
    44         int ww=(3-f[y])%3;//fy->y
    45         f[fx]=(w-ww+3)%3;//fx->fy
    46         */
    47         f[fx]=(f[y]-f[x]+d+3)%3;
    48       }
    49       else if ((f[x]-f[y]+3)%3!=d) cnt++;
    50     }
    51     printf("%d
    ",cnt);
    52     return 0;
    53 }
    View Code

    --------------------------------------------------------------------------------------------------------------------------------

    另外啊~我学习了一下好友的做法:x->y间的边权直接看成距离,1为x吃y,0为同类。这样一个食物链的x,y,z的关系都可直接推出!!○| ̄|_  orz~
    还有,合并父亲时,可以让y变为fx的父亲,而不是fy变为fx的父亲,这样虽然增大了树的深度,但影响不大,重要的是可以节省一些代码。

  • 相关阅读:
    pku2226 Muddy Fields
    pku3715 Blue and Red
    关于二分图的最大权匹配
    pku 2262&& pku 2739 && pku 3006
    pku2060 Taxi Cab Scheme
    pku 1486 Sorting Slides
    id、css命名规范
    Git 常用命令
    sublime text3插件使用
    Java实现数据结构栈stack和队列Queue
  • 原文地址:https://www.cnblogs.com/konjak/p/6030260.html
Copyright © 2020-2023  润新知