https://www.acwing.com/problem/content/description/2071/
每次合并的时候需要开一个新点去实现信息的无后效性,也就是合并之前的两个连通块信息是无法共享的,发现这样开点连边最后
形成一棵树,每次我们将信息传递到新点,也是两个合并点的lca,这使得最后求答案的直接求一边树上前缀和,不同子树不受影响
debug:最后合并n-1次,初始化和空间都要开两倍
// Problem: 网络分析
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2071/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
//# define int long long
#define ull unsigned long long
#define pii pair<int,int>
#define baoliu(x, y) cout << fixed << setprecision(y) << x
#define endl "\n"
#define debug1(x) cerr<<x<<" "
#define debug2(x) cerr<<x<<endl
const int N = 2e4 + 10;
const int M = 1e6 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-8;
int n, m;
int a[N];
int fa[N];
int sz[N];
vector<int>e[N];
int find(int x){
if(x!=fa[x])fa[x]=find(fa[x]);
return fa[x];
}
//并不是简单并查集,因为还涉及信息的历史性,也就是后来进入的节点是没有办法享用之前
//的信息的
//动态开点并查集+树上差分?
void dfs(int u,int fa){
sz[u]+=sz[fa];
for(auto v:e[u]){
if(v==fa)continue;
dfs(v,u);
//sz[v]+=sz[u];
}
}
void solve(){
//for(int i=1;i<=n;i++)fa[i]=i;
cin>>n>>m;
int root=n+1;
for(int i=1;i<=n*2;i++)fa[i]=i;
for(int i=1;i<=m;i++){
int op;cin>>op;
if(op==1){
int u,v;cin>>u>>v;
u=find(u),v=find(v);
if(u==v)continue;
fa[u]=fa[v]=root;
e[root].push_back(u);
e[root].push_back(v);
root++;
}
else {
int v;cin>>v;
v=find(v);
int val;cin>>val;
sz[v]+=val;
}
}
for(int i=1;i<=2*n;i++){
if(fa[i]==i)dfs(i,0);
}
for(int i=1;i<=n;i++)cout<<sz[i]<<" ";
}
int main() {
cin.tie(0);
ios::sync_with_stdio(false);
int t;
//cin>>t;
t=1;
while (t--) {
solve();
}
return 0;
}
``
标签:int,查集,long,fa,差分,开点,root,find,define
From: https://www.cnblogs.com/mathiter/p/18087608