POJ 1182 食物链

来源:互联网 发布:武汉矩阵选房系统 编辑:程序博客网 时间:2024/06/05 00:08

这题用并查集来写,且树的层数不超过2,3层最多只持续一个循环就会变成2层,因为只有三种动物,两两之间的关系只需要2层树就可以表示清楚了,那么现在当树有三层时怎么变成2层树呢?

先假设0,1,2分别为该结点与父结点表示的关系,0表示同类,1表示该结点被父结点吃,2表示该结点吃父结点。

这个表示方法是有原因的,目的是为了下面方便推公式,首先题目给出d表示关系,d==1表示同类,d==2表示y被x吃,那么的d-1==0,刚好表示同类,d-1==1刚好表示该结点被父结点吃(当d==2时,把x作为父结点)。

那么怎么算第三层与第一层结点的关系,并且把第三层上的结点变成第二层呢?

首先给出公式:(re[a]+re[pa[a]])%3==子结点对爷爷结点的关系;

a表示子结点,pa[a]表示父亲结点,这个公式是通过穷举法得出的,其中re数组表示该结点与父结点的关系,求出这个关系那么就可以把子结点挂到爷爷结点下面,就是把第三层结点放到了第二层上。

公式是这样推的  <span style="white-space:pre"></span>       父亲  儿子       儿子对爷爷的关系

  1. 0      0       (i + j)%3 = 0 
  2.                0      1       (i + j)%3 = 1 
  3.                0      2       (i + j)%3 = 2 
  4.                1      0       (i + j)%3 = 1 
  5.                1      1       (i + j)%3 = 2 
  6.                1      2       (i + j)%3 = 0 
  7.                2      0       (i + j)%3 = 2 
  8.                2      1       (i + j)%3 = 0 
  9.                2      2       (i + j)%3 = 1 

现在单个结点并到一个集合中的方法已经有了,那就要考虑集合并集合了,如果题目给出x,y的关系,并且x与y在之前并没有关系且x与y所属不同的集合,那么就不能直接把y的根结点并到x的根结点上,因为你并不知道这两个根结点之间的关系,那么现在的首要问题就是要求出根结点a(x),b(y)之间的关系。

方法如下:我们假设把y结点接到x结点的下方,再把b结点(也就是y的根结点)接到y的下方,那么根据(re[y]+re[b])%3来求出b结点与x结点的关系,在用(re[x]+re[b])%3来求出b结点与a结点的关系(此时的re[b]已经被更新过了),那样的话b结点就可以接到a结点的下方。

那么问题又来了,如何求出最开始的re[b]呢,也就是b结点与y的关系。

这里用的也是穷举法,得出勇士父结点与子结点的关系就是:re[父对子]=(3-re[子对父])%3。

这个很快就可以穷举出,这里就不穷举,那么通过这个公式就可以把上面求b与a关系的两个式子整理为一个式子,(d-1+3-re[y]+re[x])%3==b结点对a结点的关系

其中d-1==re[y]这个式子在一开始说三个标志数取法的时候就已经给出,3-re[y]则是b对y的关系。

这样就可以把两个集合并起来了。

然后是判断是否说的是假话,如果a,b结点不同,那这是之前没有表明过a与b的关系,为真话,那么就把它们所属的集合并起来。

如果a与b相同,如果d==1那么只要判断re[x]是否等于re[y]就可以了,因为是二层树,所以相等为真话,不相等为假话。

如果d==2,那么需要需要把根结点当做x的子结点,然后根据公式(re[y]+3-re[x])%3是否为1即可,如果为1,则真话,不为1则为假话。

思路来源:http://blog.csdn.net/c0de4fun/article/details/7318642/

代码:

////  main.cpp//  Richard////  Created by 邵金杰 on 16/7/23.//  Copyright © 2016年 邵金杰. All rights reserved.//#include<iostream>#include<cstdio>#include<cstring>using namespace std;const int maxn=50000+100;int pa[maxn],re[maxn];int cnt=0;int getroot(int a){    if(pa[a]==a) return a;    int t=getroot(pa[a]);    re[a]=(re[pa[a]]+re[a])%3;    pa[a]=t;    return pa[a];}void Union(int x,int y,int a,int b,int d){    pa[b]=a;    re[b]=(d-1+3-re[y]+re[x])%3;}int main(){    int n,k;    int d,x,y;    scanf("%d%d",&n,&k);    for(int i=0;i<=n;i++){        pa[i]=i;re[i]=0;    }    for(int i=0;i<k;i++)    {        scanf("%d%d%d",&d,&x,&y);        if(x>n||y>n) {cnt++;continue;}        if(d==2&&x==y) {cnt++;continue;}        int a=getroot(x);        int b=getroot(y);        if(a!=b) Union(x,y,a,b,d);        else{            if(d==1&&re[x]!=re[y]) cnt++;            if(d==2&&(re[y]+3-re[x])%3!=1) cnt++;        }    }    cout<<cnt<<endl;}



0 0