P2024 [NOI2001] 食物链

代码思路详解:

  1. 数据结构设计

    • 使用扩展的并查集,每个动物x维护三个信息:

      • x:自身

      • x + n:x的食物

      • x + 2n:x的天敌

  2. 关系处理逻辑

    • 同类关系(op==1)

      • 检查是否已经存在捕食或天敌关系

      • 合并自身、食物和天敌三个维度的信息

    • 捕食关系(op==2)

      • 检查是否已经是同类或反向捕食关系

      • 建立x吃y的关系

      • 关键点:建立x的天敌和y的食物之间的关系,维护食物链循环

  3. 关键点解释

    • merge(x + 2n, y + n)保证了食物链的循环性:

      • 如果x吃y,那么x的天敌应该吃x

      • y的食物应该被x的天敌吃

      • 这样才能形成"A吃B,B吃C,C吃A"的循环关系

  4. 假话判断

    • 所有违反上述关系约束的陈述都会被判定为假话

    • 边界条件(范围检查、自己吃自己)也会被判定为假话

参考代码

#include<bits/stdc++.h>
using namespace std;
const int N = 5E4 + 10;
int f[N * 3]; // 并查集数组:0~n-1表示自身,n~2n-1表示食物,2n~3n-1表示天敌
int n,k;

// 并查集查找函数(带路径压缩)
int find(int x) {
    if(f[x] != x) f[x] = find(f[x]);  // 路径压缩优化
    return f[x];
}

// 并查集合并函数
void merge(int x, int y) {
    int fx = find(x), fy = find(y);
    f[fy] = fx;  // 将y所在集合合并到x所在集合
}

// 检查并处理当前关系的函数
bool check(int op,int x,int y) {
    // 边界条件检查:超出范围或自己吃自己
    if(x > n || y > n || (x == y && op == 2)) return 0;
    
    if(op == 1) { // 处理同类关系
        // 检查矛盾:x和y不能是捕食关系
        if(find(x) == find(y + n) || find(y) == find(x + n)) return 0;
        // 检查矛盾:x和y不能是天敌关系
        if(find(x) == find(y + 2 * n) || find(y) == find(x + 2 * n)) return 0;
        
        // 合并同类关系
        merge(x, y);           // 自身是同类
        merge(x + n, y + n);    // 食物相同
        merge(x + 2 * n, y + 2 * n); // 天敌相同
    } 
    else { // 处理捕食关系:x吃y
        // 检查矛盾:x和y不能是同类或y吃x
        if(find(x) == find(y) || find(x) == find(y + n)) return 0;
        
        // 建立捕食关系
        merge(x + n, y);       // x的食物是y
        merge(y + 2 * n, x);    // y的天敌是x
        
        /* 关键点解释:x的天敌是y的食物
           这是为了保证食物链的循环性:
           - 如果x吃y,那么x的天敌应该吃x
           - 同时y的食物应该被x的天敌吃
           - 这样才能形成A吃B,B吃C,C吃A的循环关系
        */
        merge(y + n, x + 2 * n); // x的天敌是y的食物
    }
    return 1; // 关系合法
}

int main() {
    cin >> n >> k;
    // 初始化并查集,每个元素自成一个集合
    for(int i = 1; i <= 3 * n; i++) f[i] = i;
    
    int ans = 0; // 记录假话数量
    for(int i = 1; i <= k; i++) {
        int op,x,y;
        cin >> op >> x >> y;
        if(!check(op,x,y)) ans++; // 如果关系不合法,假话数+1
    }
    cout << ans;
    return 0;
}
posted @ 2025-04-23 22:24  CRt0729  阅读(6)  评论(0)    收藏  举报