小明正在做一个网络实验。
他设置了 n n n 台电脑,称为节点,用于收发和存储数据。
初始时,所有节点都是独立的,不存在任何连接。
小明可以通过网线将两个节点连接起来,连接后两个节点就可以互相通信了。
两个节点如果存在网线连接,称为相邻。
小明有时会测试当时的网络,他会在某个节点发送一条信息,信息会发送到每个相邻的节点,之后这些节点又会转发到自己相邻的节点,直到所有直接或间接相邻的节点都收到了信息。
所有发送和接收的节点都会将信息存储下来。
一条信息只存储一次。
给出小明连接和测试的过程,请计算出每个节点存储信息的大小。
输入格式
输入的第一行包含两个整数 n,m,分别表示节点数量和操作数量。
节点从 1 1 1 至 n n n 编号。
接下来 m m m 行,每行三个整数,表示一个操作。
如果操作为 1 a b
,表示将节点
a
a
a 和节点
b
b
b 通过网线连接起来。当
a
=
b
a = b
a=b 时,表示连接了一个自环,对网络没有实质影响。
如果操作为 2 p t
,表示在节点
p
p
p 上发送一条大小为
t
t
t 的信息。
输出格式
输出一行,包含
n
n
n 个整数,相邻整数之间用一个空格分割,依次表示进行完上述操作后节点
1
1
1 至节点
n
n
n 上存储信息的大小。
数据范围
1
≤
n
≤
10000
,
1≤n≤10000,
1≤n≤10000,
1
≤
m
≤
1
0
5
,
1≤m≤10^5,
1≤m≤105,
1
≤
t
≤
100
1≤t≤100
1≤t≤100
输入样例1:
4 8
1 1 2
2 1 10
2 3 5
1 4 1
2 2 2
1 1 2
1 2 4
2 2 1
输出样例1:
13 13 5 3
本题做法参考至题解
单纯用并查集这道题是没有办法解决的,因为不光需要合并集合,同时也要求了查询某一点。
对于此可以使用树上差分。
可以在合并集合的过程中同时建立起多颗树,这时候就可以保证信息量的加法仅加在集合中,并且能过查询单点。
在增加信息量的操作时,我们先给那些建立的根节点加上,然后再最后遍历所有根节点,让每棵树的值通过根节点一步步传递到叶节点,最终打成连通增加信息量的效果。
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e5 + 10;
const int M = N << 1;
int h[N], e[M], ne[M], idx;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int n, m;
int p[N];
int cnt[N];
int find(int x) {
if (p[x] != x)p[x] = find(p[x]);
return p[x];
}
void dfs(int u, int f) {
cnt[u] += cnt[f];//cnt[u]:传入的节点的值
//cnt[f]:传入的父节点的值,可以观察到后面每一层传入的都是u,都是根节点
//
//遍历树,并且让每一颗树的子树节点都加上父节点存储的值
for (int i = h[u]; i != -1; i = ne[i]) {
dfs(e[i], u);//搜下一层
}
}
int main() {
memset(h, -1, sizeof h);
cin >> n >> m;
for (int i = 1; i <= n * 2; i++)p[i] = i;
int root = n + 1;
while (m--) {
int op, a, b; cin >> op >> a >> b;
if (op == 1) {
a = find(a), b = find(b);
//这里要提前把a和b的祖宗提取出来,不然在下边建树的过程中会导致爆栈(不知道为啥)
if (a != b) {
p[a] = p[b] = root;
add(root, a);
add(root, b);
root++;//之后来的点建树就要另换一颗树
}
}
else {
cnt[find(a)] += b;//这里先只让祖宗节点加
}
}
//扫所有的根节点
for (int i = n + 1; i < root; i++)
if (p[i] == i) //如果这个根节点的祖先是自己,也就是说他是最根部的根节点
dfs(i, 0); //那就扫遍整个树,并且使这棵树上所有的点加上自己的信息量,然后再一层层传递到叶节点
//这里需要加一层判断是因为上述过程中n+1之后的节点也有可能被合并到其他的根节点上
//传入i,0,因为根节点自己不需要加,i作为根节点传入
for (int i = 1; i <= n; i++)cout << cnt[i] << " ";
return 0;
}