【网络分析】并查集/树上差分

2069. 网络分析

题目描述

给出一个 n n n个孤立点的图,每个点上的权值都是 0 0 0,进行 m m m 次操作
操作 1 :把两个点所在的连通块合并起来
操作 2 :向某个点所在的连通块的所有点累加一个值
n ≤ 1 0 4 , m ≤ 1 0 5 , n ≤ 1 0 4 , m ≤ 1 0 5 n≤10^4,m≤10^5,n≤10^4,m≤10^5 n104,m105,n104,m105
最后输出每个点上的权值

解题思路

  • 并查集 树上差分

我们通过并查集合并连通块,保证同一个连通块内的点同属一个集合
对于每一个合并操作,找到两个点所属的集合
如果这两个点不在同一连通块,那么我们构造一个新点,使这个新点成为集合合并后的根节点

这样进行 k 次有效合并操作后,就会产生 k 个新点
我们所构造的图是若干棵树,编号为 1−n 的节点都是树的叶子节点

对于每次连通块累加操作,我们只需要向集合的根节点累加一个值即可
最后对我们所构造出来的一堆树DP(只是遍历一下),把每个点的权值下放到子树中的所有节点中
然后依次输出编号为 1−n 的节点的权值即可

代码实现

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 200010, M = N << 1;
int h[N], e[M], ne[M], idx;
void add(int a, int b)  // 添加一条边a->b
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

int n, m;

int p[N];
int find(int x) {
    if (x != p[x]) {
        p[x] = find(p[x]);
    }
    return p[x];
}

int f[N];
void dfs(int u, int fa) {
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        f[j] += f[u];
        dfs(j, u);
    }
}

int main()
{
    cin >> n >> m; memset(h, -1, sizeof h);
    for (int i = 0; i < N; i++) p[i] = i;
    
    int root = n + 1;
    
    int op, a, b;
    while (m -- ) {
        cin >> op >> a >> b;
        if (op == 1) {
            int fx = find(a), fy = find(b);
            if (fx != fy) {
                add(root, fx), add(root, fy);
                p[fx] = p[fy] = root;
                root++;
            }
        } else {
            int fx = find(a);
            f[fx] += b;
        }
    }
    for (int i = n + 1; i < root; i++) if (p[i] == i) dfs(i, 0);
    for (int i = 1; i <= n; i++) cout << f[i] << ' '; cout << endl; 
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ღCauchyོꦿ࿐

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值