#3038:How Many Answers Are Wrong (带权并查集)

HDU 3038

第一次接触带权并查集

//带权并查集 更新父节点的同时更新权值
#include<bits/stdc++.h>
using namespace std;

const int N = 200020;
int pre[N];
int cnt[N];//cnt[i]代表i到自己祖先的权值

int Find(int x) {//路径压缩,更新父节点和权值
    if (x != pre[x]) {
        int fa = pre[x];
        pre[x] = Find(pre[x]);
        //当合并祖先时,x到新祖先的权值等于x到旧祖先的权值加旧祖先到新祖先的权值(此处的cnt[x]还没更新到结点所以还是到旧祖先的权值)
        cnt[x] += cnt[fa];
    }
    return pre[x];
}

int main() {
    int n, m, x, y, w, s;
    while (scanf("%d%d", &n, &m) != EOF) {
        s = 0;
        for (int i = 0; i <= n; i++) {
            pre[i] = i;
            cnt[i] = 0;
        }
        while (m--) {
            scanf("%d%d%d", &x, &y, &w);
            int fx = Find(x - 1);//因为[a, b]中包含a,所以实际上是对(a, b]操作,即x = x-1
            int fy = Find(y);
            //cout << "fx = " << fx << " fy = " << fy << endl;
            if (fx == fy) {//如果发现该区间的权值和之前得出的权值不等,s++
                if (cnt[x - 1] + w != cnt[y])
                    s++;
            }
            /*
            在合并操作中,对我们需要更新cnt[fb](由于fy成为fa的子节点),公式:cnt[fy] = cnt[x - 1] - cnt[y] + w
            - 更新cnt[fy]的目的是维护子树(fy)相对于父亲树(fx)之间的权值差。
            - cnt[y]保存结点y到结点fy之间的权值; cnt[x-1]保存结点x-1到结点fx之间的权值;w是结点x-1到结点y之间的权值
            - 所以cnt[fy]的值=从结点fa到结点x-1的权值(+cnt[x - 1]),加上从结点x到结点y的权值(+w),最后减去结点fy到结点y的权值(-cnt[y])
            */
            else {
                pre[fy] = fx;
                //cout << "pre[fy] = " << pre[fy] << endl;
                cnt[fy] = cnt[x - 1] + w - cnt[y];
                //cout << "cnt[fy] = " << cnt[fy] << endl;
            }
        }
        printf("%d
", s);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/RioTian/p/12874867.html