扩展域并查集

扩展域并查集就是指:将并查集的状态由基本的:几个元素属于朋友,则放入并查集;
扩展为更多状态。

更多通过题目来理解把。

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 句话,输出假话的总数。

思路

可以将状态分为:
同类,食物,天敌
也就是说,将状态扩展为:

  • 同类:如果a和b是同一类,则将a和b合并到一个并查集

  • 食物:如果a是b的食物,则将a和b合并到一个并查集

  • 天敌:如果a是b的天敌,则将a和b合并到一个并查集

将状态扩展后,相应的合并也要修改。

  • 如果a和b是同类,那么
    a的天敌、同类、食物也是b的天敌、同类、食物。

  • 如果a是b的天敌,那么
    a的天敌是b的食物;
    a的同类是b的天敌;
    a的食物是b的同类。

然后再判断题目的几个条件

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

直接判断即可

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

当输入为同类时,直接判断即可

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

如果输入a是b的同类,那么需要确定以下条件,才能算对

  • a的天敌没有b
  • a的食物没有b

不需要判断:a的同类是不是b的同类

如果输入a是b的天敌,那么需要确定以下条件,才能算对

  • a的同类没有b
  • a的天敌没有b

不需要判断:a的食物是不是b的同类

实现

就是用3倍的并查积的存各种动物的关系:一倍存本身,二倍存食物,三倍存天敌。

每次维护三个并查积的关系就可以了
具体看代码吧

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
const int N =1e5 + 10;
int fa[N*3];
int n,m;
int find(int x){
    if(fa[x]!=x) fa[x]=find(fa[x]);
    return fa[x]; 
}
void slove()
{
    cin>>n>>m;
    for(int i=1;i<=3*n;++i) fa[i]=i;
    int ans=0;
    int x1, x2, x3, y1, y2, y3;
    for(int i=1;i<=m;i++){
        int a,x,y;cin>>a>>x>>y;
        if(x>n || y>n){
             ans++;
             continue;
        } 
        x1 = find(x);//同类
        x2 = find(x + n);//食物
        x3 = find(x + n * 2);//天敌
        y1 = find(y);
        y2 = find(y + n);
        y3 = find(y + n * 2);
        if(a==1){
            //如果1是2的天敌或猎物,显然为谎言
            if(x2==y1 || x3==y1){
               ans++;
               continue;
            }
            fa[x1]=y1 , fa[x2]=y2 , fa[x3]=y3;
        }
        else{
            //如果1是2的同类或猎物,显然为谎言
            if(x1==y1 || x3==y1){
                ans++;
                continue;
            }
            fa[x2] = y1 , fa[x3]=y2 , fa[x1]=y3;
        }
    }
    cout<<ans<<endl;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    // cin >> t;
    while (t--)
        slove();
    return 0;
}

posted @ 2022-09-02 17:42  kingwzun  阅读(700)  评论(0编辑  收藏  举报