zoukankan      html  css  js  c++  java
  • 【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的父亲,这样虽然增大了树的深度,但影响不大,重要的是可以节省一些代码。

  • 相关阅读:
    【Nginx】ngx_event_core_module模块
    ELMAH--Using HTTP Modules and Handlers to Create Pluggable ASP.NET Components 77 out of 90 rated th
    nyist oj 214 单调递增子序列(二) (动态规划经典)
    java 入门书籍(java7)
    ARCGIS将WGS84坐标投影到高斯平面
    【linux】linux下对java程序生成dump文件,并使用IBM Heap Analyzer进行分析,查找定位内存泄漏的问题代码
    【springboot】【socket】spring boot整合socket,实现服务器端两种消息推送
    【linux】linux修改open file 大小
    【docker】docker限制日志文件大小的方法+查看日志文件的方法
    【docker】docker部署spring boot服务,但是docker logs查看容器输出控制台日志,没有日志打印,日志未打印,docker logs不打印容器日志
  • 原文地址:https://www.cnblogs.com/konjak/p/6030260.html
Copyright © 2011-2022 走看看