zoukankan      html  css  js  c++  java
  • 【题解】食物链

    题目

    题目来源:CCF NOI2001;(模拟赛 T3)

    评测地址:Luogu#2024

    题目描述

    动物王国中有三类动物 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) 句话,输出假话的总数。

    输入格式

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

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

    输出格式

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

    数据规模和约定

    评测时间限制 (1000 extrm{ms}),空间限制 (128 extrm{MiB})

    (1le Nle 5 imes 10^4)(1le Kle 10^5)

    分析

    题意是指有一组元素,每个元素有 (3) 种状态。现在给出一系列元素的状态依赖关系,形如「如果 (A)xxx,那么 (B) 就是 yyy,反之亦然。」

    (考虑一下为什么这与原题是等价的,这很重要)

    也就是说,这道题要求我们维护一个状态,其中有若干状态是 必须并存 的。这就是一个集合合并问题,可以使用并查集。

    我们将一个动物拆分成 (3) 种状态,每一种就是一个节点。我们用并查集维护其中的并存关系。

    为表示方便,下面用 (A_t) 表示一个节点:

    • A 表示节点编号,也就是动物编号 (i)
    • t 表示对应的类型,是 A、B、C 中的一个。

    比如说 (i)(j) 是同类,那么就可以合并 (i_A)(j_A)(i_B)(j_B),以及 (i_C)(j_C)

    那么如果 (i)(j),那么就可以合并 (i_A)(j_B)(i_B)(j_C),以及 (i_C)(j_A)

    判断是否真假,只需要看看除了给出条件以外的 (i)(j) 节点是否存在同一集合的情况。有就是假话。

    比如说已经知道了 (i)(j)(j)(k) 又是同类,可以得到 (i_A)(k_B) 在一个集合内。此时如果说给出 (k)(i)(即 (k_B)(i_C) 在同一集合内),就会矛盾。

    这样,我们就可以利用并查集,在 (Theta(nalpha(n))) 的时间内愉快 AC。

    Code

    由于要判断,这次的代码有亿点长……

    #include <cstdio>
    #include <cctype>
    using namespace std;
    
    const int max_n = 50000;
    int ufs[max_n*3]; // 并查集别忘了开 3 倍大小
    
    inline int pack(int id, int lat) { return id + lat * max_n; } // 编号
    
    int find(int x) // 查找,最好加上路径压缩
    {
    	if (x != ufs[x])
    		ufs[x] = find(ufs[x]);
    	
    	return ufs[x];
    }
    
    void unite(int a, int b) // 合并
    {
    	ufs[find(a)] = find(b);
    }
    
    bool isn_1(int a, int b) // 是否(不)是同类
    {
    	if (find(pack(a, 0)) == find(pack(b, 1)))
    		return true;
    	if (find(pack(a, 0)) == find(pack(b, 2)))
    		return true;
    	if (find(pack(a, 1)) == find(pack(b, 0)))
    		return true;
    	if (find(pack(a, 1)) == find(pack(b, 2)))
    		return true;
    	if (find(pack(a, 2)) == find(pack(b, 0)))
    		return true;
    	if (find(pack(a, 2)) == find(pack(b, 1)))
    		return true;
    	
    	return false;
    }
    
    bool isn_2(int a, int b) // 是否(不)是 a 吃 b
    {
    	if (find(pack(a, 0)) == find(pack(b, 0)))
    		return true;
    	if (find(pack(a, 0)) == find(pack(b, 2)))
    		return true;
    	if (find(pack(a, 1)) == find(pack(b, 0)))
    		return true;
    	if (find(pack(a, 1)) == find(pack(b, 1)))
    		return true;
    	if (find(pack(a, 2)) == find(pack(b, 1)))
    		return true;
    	if (find(pack(a, 2)) == find(pack(b, 2)))
    		return true;
    	
    	return false;
    }
    
    inline int read()
    {
    	int ch = getchar(), t = 1, n = 0;
    	while (isspace(ch)) { ch = getchar(); }
    	if (ch == '-') { t = -1, ch = getchar(); }
    	while (isdigit(ch)) { n = n * 10 + ch - '0', ch = getchar(); }
    	return n * t;
    }
    
    int main()
    {
    	int n = read(), k = read(), opt, ta, tb, ans = 0;
    
    	for (int i = 0; i < n; i++) // 并查集初始化
    	{
    		ufs[pack(i, 0)] = pack(i, 0);
    		ufs[pack(i, 1)] = pack(i, 1);
    		ufs[pack(i, 2)] = pack(i, 2);
    	}
    
    	for (int i = 0; i < k; i++)
    	{
    		opt = read(), ta = read() - 1, tb = read() - 1;
    
    		if (ta < 0 || ta >= n || tb < 0 || tb >= n) // 越界
    		{
    			ans++;
    			continue;
    		}
    
    		if (opt & 1) // 同类标记
    		{
    			if (isn_1(ta, tb)) // 谎言
    			{
    				ans++;
    				continue;
    			}
    
    			unite(pack(ta, 0), pack(tb, 0)); // 更新
    			unite(pack(ta, 1), pack(tb, 1));
    			unite(pack(ta, 2), pack(tb, 2));
    		}
    		else // 梅开二度
    		{
    			if (isn_2(ta, tb))
    			{
    				ans++;
    				continue;
    			}
    
    			unite(pack(ta, 0), pack(tb, 1));
    			unite(pack(ta, 1), pack(tb, 2));
    			unite(pack(ta, 2), pack(tb, 0));
    		}
    	}
    
    	printf("%d
    ", ans); // 精准结束
    
    	return 0;
    }
    
  • 相关阅读:
    CMSIS RTOS RTX License
    ssh无法登录linux服务器的解决办法
    蓝牙利用协议栈API读取设备MAC地址
    ARM Cortex-M3 MCU的I2C DMA操作和中断
    ARM RTX操作系统—Theory of Operation—System Resources & Scheduling Options
    ARM RTX操作系统—Theory of Operation—System Task Manager & Task Management
    ARM RTX操作系统—Theory of Operation—Timer Tick Interrupt
    ARM RTX操作系统—Overview—Advantages
    静态代理和动态代理
    异常未处理,该怎么处理?
  • 原文地址:https://www.cnblogs.com/5ab-juruo/p/solution-noi2001-lg2024.html
Copyright © 2011-2022 走看看