240. 食物链——并查集扩展域&带边权

这篇博客详细解析了Acwing240题目的解题思路,主要涉及奇偶游戏在不规则三角形结构中的应用,以及如何使用扩展域和边权并查集解决此类问题。文章通过代码示例解释了如何处理不同情况,包括集合合并、关系判断等关键步骤,帮助读者理解并查集在解决此类问题中的作用。
摘要由CSDN通过智能技术生成

题面

Acwing240
在这里插入图片描述在这里插入图片描述

思路

思路跟奇偶游戏一样;

不过这里是一个三角结构,不是对称的,需要注意一下;

扩展域并查集

将每个元素分裂成三个条件;

x + B x+B x+B表示 x x x的食物, x + 2 B x+2B x+2B表示 x x x的天敌

Code

#include <iostream>

using namespace std;

const int N = 5e4+10;

const int M = 3*N,B = 5e4;

int n,m,p[M];
int _find(int x){
    if(x == p[x]) return x;
    return p[x] = _find(p[x]);
}
int main(){
    cin >> n >> m;
    for(int i=1;i<M;++i) p[i] = i;
    int ans = 0;
    int d,x,y;
    while(m--){
        cin >> d >> x >> y;
        if(x>n || y>n){
            ++ans;
            continue;
        }
        if(d == 1){
            if(_find(x+B) == _find(y)||
               _find(x+2*B) == _find(y)){
                ++ans;
            }else{
                //merge
                p[_find(x)] = _find(y);
                p[_find(x+B)] = _find(y+B);
                p[_find(x+2*B)] = _find(y+2*B);
            }
        }else{
            if(x == y){
                ++ans;
                continue;
            }
            if(_find(y) == _find(x)||
               _find(y) == _find(x+2*B)){
                ++ans;
            }else{
                p[_find(x)] = _find(y+2*B);
                p[_find(x+B)] = _find(y);
                p[_find(x+2*B)] = _find(y+B);
            }
        }
    }
    cout << ans << '\n';
    return 0;
}

边权并查集

我们规定 d ( i ) d(i) d(i)表示点 i i i与其父亲的关系

分为三类,我们用向量来表示两个点之间的关系;

  • 0为同类
  • 1为食物
  • 2为天敌

x , y x,y x,y在同一个集合中,如下图所示;在这里插入图片描述
x x x y y y时, x y = 2 xy=2 xy=2,表示 y y y x x x吃;

x x x y y y同级, x y = 0 xy=0 xy=0,表示 y y y x x x同级;


x , y x,y x,y不再同一集合中,我们需要合并;

那么问题就是求解下图中的 d ( p x ) , p x 是 x 的 父 节 点 d(px),px是x的父节点 d(px),pxx在这里插入图片描述
我们设 x y xy xy之间的关系为param

同类, p a r a m = 0 param=0 param=0

y y y x x x吃, p a r a m = 2 param=2 param=2

很容易想到 d y − ( d x + d p x ) = p a r a m d_y-(d_x+d_{px}) = param dy(dx+dpx)=param(就是向量的表示而已)

解出 d p x d_{px} dpx即可完成merge

Code

#include <iostream>

using namespace std;

const int N = 5e4+10;

int n,m,p[N],d[N];

int _find(int x){
    if(x != p[x]){
        int root = _find(p[x]);
        d[x] = (d[x] + d[p[x]])%3;
        p[x] = root;
    }
    return p[x];
}
int main(){
    cin >> n >> m;
    for(int i=1;i<N;++i) p[i] = i,d[i]=0;
    int ans = 0;
    int op,x,y,param;
    while(m--){
        param = 0;
        cin >> op >> x >> y;
        if(x>n || y>n){
            ++ans;
            continue;
        }
        if(op == 2 && x == y){
            ++ans;
            continue;
        }
        if(op == 2) param = 2;
        int px = _find(x),py = _find(y);
        if(px == py){
            if(((d[y]-d[x])%3+3)%3 != param) ++ans;
        }else{
            //merge
            d[px] = ((d[y]-d[x]-param)%3+3)%3;
            p[px] = py;
        }
    }
    cout << ans << '\n';
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值