b_lq_网络分析(两个点不在同一连通块就新开一个根 | 带权并查集优化)

在某个节点发送一条信息,信息会发送到每个相邻的节点,之后这些节点又会转发到自己相邻的节点,直到所有直接或间接相邻的节点都收到了信息。
所有发送和接收的节点都会将信息存储下来。一条信息只存储一次。
给出小明连接和测试的过程,请计算出每个节点存储信息的大小。

这题要跟着输入数据一步一步模拟才不容易出错,就是你不能连完边再跑逻辑;
这是最暴力的方法(超时)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m,fa[N],ans[N];
vector<int> g[N];
int find(int u) {return fa[u]==u ? u : fa[u]=find(fa[u]);}
int main() {
    scanf("%d%d", &n,&m);
    for (int i=1; i<N; i++) fa[i]=i;
    while (m--) {
        int t,u,v; scanf("%d%d%d", &t,&u,&v);
        if (t==1) {
            fa[find(u)]=find(v);
        } else {
            int fu=find(u);
            for (int i=1; i<=n; i++) if (find(i)==fu)
                ans[i]+=v;
        }
    }
    for (int i=1; i<=n; i++) printf("%d ",ans[i]);
    return 0;
}

优化思路
先让一个附加的公共根统率一堆连通块的结点,初始情况下发送数据包时只让这个公共根存储,最后树形遍历的时候,就让公共根发送自己存储的数据包给子节点存储

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m,fa[N],d[N];
vector<int> g[N];
int find(int u) {return fa[u]==u ? u : fa[u]=find(fa[u]);}
void dfs(int u, int fa) {
    d[u]+=d[fa];
    for (int v : g[u]) if (fa!=v)
        dfs(v,u);
}
int main() {
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin>>n>>m;
    int root=n+1;
    for (int i=1; i<N; i++) fa[i]=i;
    for (int i=0; i<m; i++) {
        int op,u,v; cin>>op>>u>>v;
        if (op==1) {
            int fu=find(u), fv=find(v);
            if (fu!=fv) {
                fa[fu]=fa[fv]=root;
                g[root].push_back(fu);
                g[root++].push_back(fv); //每次只要两个点不在一个连通块上,就新开一个连通块;这样既可保证未连接的点收不到"别人"发的数据,又可保证后面的点合并之后,数据的可接受性,且只接受后面合并后发送的数据
            }
        } else {
            d[find(u)]+=v;
        }
    }
    for (int i=n+1; i<root; i++) if (fa[i]==i) dfs(i,0);  //如果i是根,则将i的数据扩散到i的子节点
    for (int i=1; i<=n; i++) printf("%d ", d[i]);
    return 0;
}

更优的做法是:用带权并查集(用的还不是很熟),复杂度是 mlogn

原文地址:https://www.cnblogs.com/wdt1/p/13779661.html