• POJ1182 【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句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
    1) 当前的话与前面的某些真的话冲突,就是假话;
    2) 当前的话中X或Y比N大,就是假话;
    3) 当前的话表示X吃X,就是假话。
    你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。

    Input
    第一行是两个整数N和K,以一个空格分隔。
    以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
    若D=1,则表示X和Y是同类。
    若D=2,则表示X吃Y。
    Output
    只有一个整数,表示假话的数目。

    Sample Input
    100 7
    1 101 1
    2 1 2
    2 2 3
    2 3 3
    1 1 3
    2 3 1
    1 5 5
    Sample Output
    3

    标签:种类并查集

    逻辑推理的题有一部分和并查集有关,此题是种类并查集的经典例题。
    首先我们把每个动物分成三个点,对于点i,点i表示第i个动物的种类,点i+n表示第i个动物的食物,点i+2n表示第i个动物的天敌。
    这样一来,提供信息:x和y同类,相当于提供三条信息:
    1.x、y在同一个集中
    2.x+n、y+n在同一个集中
    3.x+2n、y+2n在同一个集中
    于是我们merge(x, y), merge(x+n, y+n), merge(x+2n, y+2n);
    同理,提供信息:x吃y,相当于提供三条信息:
    1.x、y+2n在同一个集中
    2.x+n、y在同一个集中
    3.x+2n、y+n在同一个集中
    于是我们merge(x, y+2n), merge(x+n, y), merge(x+2n, y+n);
    如果在接到信息x和y同类后,发现x和y+n同类或x和y+2n同类,则此信息与先前信息矛盾。因为对称性,我们不用再判断y是否和x+n或x+2n同类。同理可处理x吃y的情况。

    最后附上AC代码:

    #include <iostream>
    #include <cstdio>
    #define MAX_N 50000
    using namespace std;
    int n, k, f[MAX_N*3+5], cnt = 0;
    int get(int x) {
    	if (x != f[x])	f[x] = get(f[x]);
    	return f[x];
    }
    inline void merge(int x, int y) {
    	int ancx = get(x), ancy = get(y);
    	if (ancx != ancy)	f[ancx] = ancy;
    	return;
    }
    int main() {
    	scanf("%d%d", &n, &k);
    	for (int i = 1; i <= n*3; i++)	f[i] = i;
    	for (int i = 0; i < k; i++) {
    		int s, x, y;
    		scanf("%d%d%d", &s, &x, &y);
    		if (x > n || y > n) {
    			cnt++;
    			continue;
    		}
    		if (s == 1) {
    			if (get(x) == get(y+n) || get(x) == get(y+2*n)) {
    				cnt++;
    			} else {
    				merge(x, y);
    				merge(x+n, y+n);
    				merge(x+2*n, y+2*n);
    			}
    		} else {
    			if (x == y || get(x) == get(y) || get(x) == get(y+n)) {
    				cnt++;
    			} else {
    				merge(x, y+2*n);
    				merge(x+n, y);
    				merge(x+2*n, y+n);
    			}
    		}
    	}
    	printf("%d", cnt);
    	return 0;
    }
    
  • 相关阅读:
    Go语言之依赖管理
    Go之NSQ
    Redis相关
    Go语言操作mongoDB
    Go语言操作Redis
    mysql-5.7.22-winx64.zip 安装
    LL(1)文法系列(二)预测分析表
    LL(1)文法系列(三)预测分析程序
    LL(1)文法系列(一)first集和follow集
    算符优先系列之(二)算符优先关系表
  • 原文地址:https://www.cnblogs.com/AzraelDeath/p/7561765.html
Copyright © 2020-2023  润新知