这题用并查集来写,且树的层数不超过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> 父亲 儿子 儿子对爷爷的关系
- 0 0 (i + j)%3 = 0
- 0 1 (i + j)%3 = 1
- 0 2 (i + j)%3 = 2
- 1 0 (i + j)%3 = 1
- 1 1 (i + j)%3 = 2
- 1 2 (i + j)%3 = 0
- 2 0 (i + j)%3 = 2
- 2 1 (i + j)%3 = 0
- 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;
}