食物链
时间限制:1000 ms | 内存限制:65535 KB
描述
动物王国中有三类动物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),输出假话的总数。
输入
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
输出
只有一个整数,表示假话的数目。
样例输入
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
样例输出
3
这道题很经典,加深了我对并查集的理解与应用,去网上看的代码,有篇博客非常简洁,解题关键代码就只有7.8行的样子吧。没有很详细的注释,就这7,8行,看了我足足6.7小时,
收获挺大,我想把这题关键的部分写下来,纪念一下我的劳动,顺便也许能帮到人吧。
这是我参考的一篇博客:很详细,但是说得有点太多了,我都看得累了,代码也有点多了。。。下面的代码才是我说的只有7行关键的简洁代码,我写的类似与那篇博客
http://blog.csdn.net/c0de4fun/article/details/7318642
这道题的代码很精巧,先说总体思路
刚拿到这题,我想的是能不能将之化为3个并查集ABC,然后每个并查集头结点都记录着这类动物是什么属性,然后发现,根本行不通,因为不好确定到底是哪种属性的动物
这个的思路是将所有说到的动物都放在一个并查集,就是说只要有关系,就放一起。
find_head()找了头结点还压缩了路径到只有两层,这样就很容易判断两者的关系了,如果是同一个头结点通过对头结点的关系r[ ]很简单,
如果不在一个头结点,就说明没有关系嘛。肯定是真话,然后直接接起来就行了。接起来要自己推导个公式,难住我很久。。。等下说
f[N]是存某号动物的父节点的编号,r[N]是对父节点的关系 0 同类,1被父节点吃,2吃父节点
find_head()函数递归不但找了头结点,还将搜索路径上的节点都压缩路径,将它直接连接到了头结点,对递归熟悉的不难理解
关键在于确定对 爷爷节点 的那个公式上,穷举一下,然后就能很轻松的看出来的,像个三角形关系
r[f[x]] r[x]
爷爷 父亲 儿子 儿子与爷爷
0 0 0 (r[x] +r[f[x]] )%3
0 1 1
0 2 2
1 0 1
1 1 2
1 2 0
2 0 2
2 1 0
2 2 1
然后到了将并查集联合这个函数,
前面说了,用find_head()找头结点,并且直接连上头结点,然后判断fx,fy是否相等,等于就是直接连起来对吧,但是发现个四边形关系,所以化成两个三角形呗。
3-r[y]是 y的头结点 对 y 的关系,这个好理解吧,
d-1是 y 对 x 的关系,因为 d 原本是 x 对 y 的关系,减 1 正好反过来。
然后用上我们前面的find里面的那个对父节点的公式, (r[x] +r[f[x]] )%3 ,带进去,不久得到 y 的头结点对 x 的关系了,
然后再加上 x 对 x 的父节点的关系就得到了fy对fx的关系, 这不就分成了2个三角形,得到了 r[fy] =什么。
如果fx==fy,就说明x和y有个关系,因为只有两层,所以自己再枚举一下,就得到了这个公式 (r[y]-r[x]+3)%3!=d-1;
或者你足够理解那个find_head()的那个公式3-r[x]是 x 的头结点对 x 的关系与r[y]相加一下就是 y 对 x 的关系看是否等于d-1。
写完博客,发现更加理解这题了,也更加理解怎么推导公式啥的了,有收获。
1 #include <stdio.h> 2 3 int const N=50005; 4 int f[N]; 5 int r[N]; 6 7 int find_head(int x) 8 { 9 int fx=x; 10 if (x!=f[x]) 11 { 12 fx=find_head(f[x]); 13 r[x]=(r[x]+r[f[x]])%3; //这是穷举可以轻松看出来的公式,压缩路径 14 f[x]=fx; 15 } 16 return f[x]; 17 } 18 19 int Union(int d,int x,int y) 20 { 21 22 int fx=find_head(x); 23 int fy=find_head(y); 24 if (fx!=fy) 25 { 26 f[fy]=fx; 27 r[fy]=(3-r[y]+d-1+r[x])%3; //很重要的一个公式,由find里面的公式推导过来 28 return 0; 29 } 30 if ((r[y]-r[x]+3)%3!=d-1) return 1; 31 return 0; 32 } 33 34 int main() 35 { 36 int n,k; 37 int d,x,y; 38 int ans=0; 39 scanf("%d%d",&n,&k); 40 for (int i=1;i<=n;i++) 41 { 42 f[i]=i; 43 r[i]=0; 44 } 45 while (k--) 46 { 47 scanf("%d%d%d",&d,&x,&y); 48 if (x>n||y>n||(x==y&&d==2)) 49 { 50 ans++; 51 continue; 52 } 53 if (Union(d,x,y)) ans++; 54 } 55 printf("%d\n",ans); 56 return 0; 57 }