洛谷P2024 [NOI2001] 食物链 题解

洛谷P2024 [NOI2001] 食物链 题解

题目链接: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 句话,输出假话的总数。

1 ≤ N ≤ 5 × 1 0 4 ,   1 ≤ K ≤ 1 0 5 1 \le N \le 5\times10^4,~1 \le K \le 10^5 1N5×104, 1K105

考虑种类并查集。

我们维护三个并查集,分别存同类、食物和天敌

这里可以用1倍~3倍来划分,即 xx+nx+2*n

例如 xx+n 合并,表示 x 及其同类均能吃 x+n 及其同类

不用A,B,C作为划分依据是因为题目没说每个动物的种类

并且在本题中,准确的种类并没有什么用处。

对于「X和Y是同类」,看上去只要把同类的并查集合并

但其实其他两个并查集也要合并,因为有可能X存在已知的天敌

而X和Y是同类,所以Y也有这个(些)天敌

同样的,对于「X吃Y」,也需要对两两关系进行处理

时间复杂度 O ( m ) O(m) O(m)

代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(5e4+15)

int n,Q,f[N<<2];
void init(int n){for(int i=1; i<=n; i++) f[i]=i;}
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
void merge(int u,int v){f[find(u)]=find(v);}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> Q;
    int res=0; init(3*n);
    for(int op,x,y; Q--;)
    {
        cin >> op >> x >> y;
        if(x>n||y>n){++res; continue;}
        if(op==1)
        {
            if(find(x)==find(y+n)||find(x)==find(y+2*n)){++res; continue;}
            merge(x,y); merge(x+n,y+n); merge(x+2*n,y+2*n);
        }else
        {
            if(find(x)==find(y)||find(x)==find(y+n)){++res; continue;}
            merge(x,y+2*n); merge(x+n,y); merge(x+2*n,y+n);
        }
    }
    cout << res << '\n';
    return 0;
}

参考文献

[1] [https://www.luogu.com.cn/blog/DanKuroto/solution-p2024](

转载请说明出处

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值