#include<iostream>
#include<cstdio>
#include<cmath>
#include<string>
using namespace std;
const int N = 1e4 + 10;
typedef long long ll;
int n, m;
int op, a, b;
int fa[N], dataa[N];
int get_f (int k){
if(fa[k] == k || fa[fa[k]] == fa[k]) return fa[k];
int r = get_f(fa[k]);
dataa[k] += dataa[fa[k]];
fa[k] = r;
return r;
}
void merge(int a, int b){
int ffa = get_f(a);
int ffb = get_f(b);
if( ffa != ffb){
dataa[ffa] -= dataa[ffb];
fa[ffa] = fa[ffb];
}
}
void init(int x){
for(int i = 1; i <= x; ++i){
fa[i] = i;
}
}
int main(){
cin >> n >> m;
init(n);
for(int i = 1; i <= m; ++i){
cin >> op >> a >> b;
if(op == 1){
merge(a, b);
}
if(op == 2){
dataa[get_f(a)] += b;
}
}
for(int i = 1; i <= n; ++i){
if(get_f(i) == i) cout << dataa[i] << ' ';
else cout << dataa[i] + dataa[get_f(i)] << ' ';
}
cout << endl;
return 0;
}
带权并查集,所有的信息保存在根节点以及从叶子节点到根节点的路径上求得。所以两棵树合并的时候注意操作,儿子节点需要先减去父节点的权值,再合并。同时,并查集在做路径压缩的时候,由于是递归性质的路径压缩。所以,这条链上的节点会不断把自己挂到根节点下面,所以需要加上之前减去的节点。
注意代码的一个地方:
if(fa[k] == k || fa[fa[k]] == fa[k]) return fa[k];
遍历到第二层就退出,如果不这样的话, 第二层的节点会重复操作