题目链接:[蓝桥杯 2020 省 AB1] 网络分析 - 洛谷
解法一:并查集打暴力
不解释,肯定T
代码:
#include<iostream>
using namespace std;
const int N = 100005;
int f[N];
int cnt[N];
int op,a,b;
int n,m;
int Find(int x)
{
if(x==f[x]) return x;
return f[x]=Find(f[x]);
}
void Merge(int u,int v)
{
f[Find(u)]=Find(v);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;++i) f[i]=i;
for(int i=1;i<=m;++i)
{
cin>>op>>a>>b;
if(op==1) Merge(a,b);
else if(op==2)
{
int root=Find(a);
for(int i=1;i<=n;++i)
{
if(Find(i)==root) cnt[i]+=b;
}
}
}
for(int i=1;i<=n;++i) cout<<cnt[i]<<" ";
return 0;
}
解法二:加入新节点优化
优化:每次给连通集加上一个数时,将这个数加到根节点上,那么每个结点值大小即为从该节点到根节点上所有数之和。
改变1:Merge需要改变
若直接合并a,b两个结点,如f[a]=b(将a接到b上)那么a结点上的所有的子孙结点计算大小时,都会多加一个w[b],因此有一下两种解决方案:
1.将w[a] 减去 w[b]
2.将a和b同时接到一个权值为0的结点上。
改变2:状态压缩需要改变
若将f[a]直接等于根节点,那么计算a结点上值时,会少加一个w[f[a]],所以先将w[a] 加上一个w[f[a]] 再将f[a] 修改为根节点。
如果a就是根节点或a的父节点就是根节点,那么无需修改。
代码:
#include<iostream>
using namespace std;
const int N = 100005;
int n,m;
int k;
int op,a,b;
int f[N];
int w[N];
int Find(int x)
{
if(x==f[x]||f[x]==f[f[x]]) return f[x];
int r=Find(f[x]);
w[x]+=w[f[x]];
f[x]=r;
return r;
}
int main()
{
for(int i=0;i<N;++i) f[i]=i;
cin>>n>>m;
k=n;
for(int i=1;i<=m;++i)
{
cin>>op>>a>>b;
if(op==1)
{
a=Find(a),b=Find(b);
if(a!=b)
{
f[a]=f[b]=++k;
}
}
else
{
a=Find(a);
w[a]+=b;
}
}
for(int i=1;i<=n;++i)
{
int r=Find(i);
if(i==r) cout<<w[i]<<" ";
else cout<<w[i]+w[r]<<" ";
}
cout<<endl;
return 0;
}
解法三:不加入新节点优化
代码:
#include<iostream>
using namespace std;
const int N = 100005;
int n,m;
int f[N];
int w[N];
int op,a,b;
int Find(int x)
{
if(x==f[x]||f[x]==f[f[x]]) return f[x];
int r=Find(f[x]);
w[x]+=w[f[x]];
f[x]=r;
return r;
}
int main()
{
cin>>n>>m;
for(int i=0;i<=n;++i) f[i]=i;
for(int i=1;i<=m;++i)
{
cin>>op>>a>>b;
if(op==1)
{
a=Find(a);
b=Find(b);
if(a!=b)
{
w[a]-=w[b];
f[a]=b;
}
}
else if(op==2)
{
int r=Find(a);
w[r]+=b;
}
}
for(int i=1;i<=n;++i)
{
int r=Find(i);
if(r==i) cout<<w[i]<<" ";
else cout<<w[i]+w[r]<<" ";
}
return 0;
}