[边带权并查集] 食物链(经典+维护到根的距离)

1. 并查集+维护到根的距离

Biu

在这里插入图片描述
并查集,多维护一个数组 d 记录当前节点到父节点的距离,初始化为 0,即自己到自己的距离为 0。当前节点到所处集合的 root 节点距离在模 3 意义下只会出现三种情况:0,1,2,我们将这三种情况分别指代为与 root 节点为 同类吃根节点被根节点吃

路径压缩:递归找到当前点的 root 节点,这一步递归操作并查集都一样,但是需要注意,递归下去如果不对 p[x] 进行记录,那么在后面更新 d[x] 的时候就全是 root 节点的结果了,所以需要提前记录下 p[x] 的当前值,便于回溯求解 d[x]

判断 xy 是否同类的真假话关系:

  • 首先判断 xy 是否在同一集合里,即到 root 节点距离是否相同
    • 如果相同,那么只需要再判断到 root 集合的距离是否相等即可,距离不等则为假话
    • 如果不同,说明 xy 还没有关系,则进行合并,放入同一集合。这里采用并查集的合并即可,但是需要注意更新 d[x] 数组,更新方式为 ( d [ x ] + ? − d [ y ] ) % 3 = 0 (d[x] + ? - d[y]) \%3 = 0 (d[x]+?d[y])%3=0 ? ? ? 表示两个根节点之间的距离,那么移项可得 ? = d [ y ] − d [ x ] ?=d[y] - d[x] ?=d[y]d[x],即 d [ p x ] = d [ y ] − d [ x ] d[px] = d[y] - d[x] d[px]=d[y]d[x] d [ p x ] d[px] d[px] d [ x ] d[x] d[x]root 节点,现在存放的是到 d [ p y ] d[py] d[py] 的距离,py 即是 px 集合的新 root 节点

同理可分析xy 的真假话关系:

  • 首先明确一点,xy 的话,x 到根的距离应该比 y 到根的距离在模 3 意义下多 1
  • 再判断 xy 是否在同一集合里,即到 root 节点距离是否相同
    • 如果相同,判断 ( d [ x ] − d [ y ] − 1 ) % 3 (d[x] - d[y] - 1)\% 3 (d[x]d[y]1)%3 结果是否为 0 即可
    • 如果不同,进行合并。更新方式为 ( d [ x ] + ? − d [ y ] − 1 ) % 3 (d[x] + ? - d[y] - 1) \%3 (d[x]+?d[y]1)%3 ,即 d [ p x ] = d [ y ] + 1 − d [ x ] d[px] = d[y] + 1 - d[x] d[px]=d[y]+1d[x]

可以参考这篇大佬题解

非注释代码:

#include <iostream>

using namespace std;

const int N = 5e5+5;

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

int find(int x) {
    if (p[x] != x) {
        int t = find(p[x]);
        d[x] += d[p[x]];
        p[x] = t;
    }
    return p[x];
}

int main() {
    cin >> n >> m;
    
    for (int i = 1; i <= n; ++i) p[i] = i;
    
    int res = 0;
    while (m --) {
        int t, x, y;
        cin >> t >> x >> y;
        
        if (x > n || y > n) res ++;
        else {
            int px = find(x), py = find(y);
            if (t == 1) {
                if (px == py && (d[x] - d[y]) % 3) res ++;
                else if (px != py) {
                    p[px] = py;
                    d[px] = d[y] - d[x];
                }
            }
            else {
                if (px == py && (d[x] - d[y] - 1) % 3) res ++;
                else if (px != py) {
                    p[px] = py;
                    d[px] = d[y] + 1 - d[x];
                }
            }
        }
    }
    
    cout << res << endl;
    
    return 0;
}

带注释代码:

#include <iostream>

using namespace std;

const int N = 5e5+5;

int n, m;
int p[N], d[N]; // p为并查集存放父节点位置,d存放到父节点的距离

int find(int x) {
    if (p[x] != x) {
        int t = find(p[x]);  // 在这里find(p[x])会吧p[x]直接搞成根节点,所以需要提前保存回溯
        d[x] += d[p[x]];
        p[x] = t;
    }
    return p[x];
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) p[i] = i;
    
    int res = 0;
    while (m --) {
        int t, x, y;
        cin >> t >> x >> y;
        
        if (x > n || y > n) res ++;
        else {
            int px = find(x), py = find(y);     // 先找到x,y的根节点
            if (t == 1) {   // 如果两者是同类
                // x,y已经在一颗树上了,x,y到根节点距离模3不同,则不为同类,为假话
                if (px == py && (d[x] - d[y]) % 3) res ++;  
                else if (px != py) {        // 不在一个集合里,执行两个集合合并的操作
                    // 记px到py为合并之间的距离为?
                    // 记x到px距离为dx,y到py的距离为dy
                    // 则若是同类,应该有(dx+?-dy)%3=0
                    // 那么 ? 就等于 dy-dx
                    p[px] = py;  
                    d[px] = d[y] - d[x];
                }
            } else {    // x吃y的话,那么x的距离应该比y多1
                if (px == py && (d[x] - d[y] - 1) % 3) res ++;
                else if (px != py) {
                    p[px] = py;
                    d[px] = d[y] + 1 - d[x];
                }
            }
        }
    }
    
    cout << res << endl;
    
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值