P2024 [NOI2001]食物链[扩展域并查集]

题目来源:洛谷

题目描述

动物王国中有三类动物 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 句话有的是真

的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。

• 当前的话与前面的某些真的话冲突,就是假话

• 当前的话中 X 或 Y 比 N 大,就是假话

• 当前的话表示 X 吃 X,就是假话

你的任务是根据给定的 N 和 K 句话,输出假话的总数。

输入输出格式

输入格式:

 

从 eat.in 中输入数据

第一行两个整数,N,K,表示有 N 个动物,K 句话。

第二行开始每行一句话(按照题目要求,见样例)

 

输出格式:

 

输出到 eat.out 中

一行,一个整数,表示假话的总数。

 

输入输出样例

输入样例#1: 
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
输出样例#1: 
3

说明

1 ≤ N ≤ 5 ∗ 10^4

1 ≤ K ≤ 10^5

 

解析:

好题啊好题啊(啪啪啪)!干了我一天终于出来了。

一开始想用边带权并查集解,发现本蒟蒻想不出来,还是用了扩展域。

 

这题可以看作 P1525 关押罪犯 的一个延伸拓展题目,具体思路是差不多的。


如果对扩展域有疑问,可以看我以前的一篇题解 P1525 关押罪犯[扩展域并查集],里面有提到扩展域。

思路:

 扩展域并查集维护三个域:x_self同类,x_enemy天敌,x_eat捕食。

他们之间的关系:

假设有x,y两个动物,

 

如果题目给定x和y是同类,那么合并x_self,y_self和x_enemy,y_enemy和x_eat和y_eat。

前提条件是:x_self与y_eat不在同一个集合,x_eat与y_self不在同一个集合。

 

如果题目给定x吃y,这里我们注意到一点,由于题目给出的食物链关系是环形,因此我们可以由“x吃y”推断出“x的天敌是y的猎物”

由此得到,合并x_eat,y_self和x_self,y_enemy和x_enemy,y_eat。

前提条件是:x_self和y_self不在一个集合,x_self和y_eat不在一个集合。

 

参考代码:

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<queue>
 4 #include<cstring>
 5 #include<iostream>
 6 #define N 50010
 7 using namespace std;
 8 struct node{
 9     int x,y,flag;
10 }g[N<<4];
11 int n,k,fa[N<<4];
12 int get(int x)
13 {
14     if(fa[x]==x) return x;
15     return fa[x]=get(fa[x]);
16 }
17 void merge(int x,int y)
18 {
19     x=get(x),y=get(y);
20     fa[x]=y;
21 }
22 int main()
23 {
24     //freopen("fuc.in","r",stdin);
25     //freopen("fuc.out","w",stdout);
26     int cnt=0;
27     scanf("%d%d",&n,&k);
28     for(int i=1;i<=n*3;i++) fa[i]=i;
29     for(int i=1;i<=k;i++)
30         scanf("%d%d%d",&g[i].flag,&g[i].x,&g[i].y);
31     for(int i=1;i<=k;i++){
32         if(g[i].x>n||g[i].y>n){
33             cnt++;continue;
34         }
35         int x_self=g[i].x,x_enemy=g[i].x+n,x_eat=g[i].x+n+n;
36         int y_self=g[i].y,y_enemy=g[i].y+n,y_eat=g[i].y+n+n;
37         if(g[i].flag==1){
38             if(get(x_eat)==get(y_self)||get(x_self)==get(y_eat)){
39                 cnt++;
40             }
41             else{
42                 merge(x_self,y_self);
43                 merge(x_eat,y_eat);
44                 merge(x_enemy,y_enemy);
45             }
46         }
47         else{
48             if(get(x_self)==get(y_self)||get(y_eat)==get(x_self)){
49                 cnt++;
50             }
51             else{
52                 merge(x_self,y_enemy);
53                 merge(x_eat,y_self);
54                 merge(x_enemy,y_eat);
55             }
56         }
57     }
58     cout<<cnt<<endl;
59     return 0;
60 }

 

posted @ 2019-06-05 18:21  DarkValkyrie  阅读(189)  评论(0编辑  收藏  举报