abc 355 F - MST Query
题目链接:https://atcoder.jp/contests/abc355/tasks/abc355_f
题目要求动态维护最小生成树.
那么我们考虑朴素的Kruskal算法:将边从小到大排序,不断加边,用并查集维护联通块,加边加到整张图联通(联通块数量为1)为止,最后的答案就是从小到大遍历边权 将边的数量*当前边权 相加起来就是答案.
那么在这道题中,动态维护最小生成树的过程中我们唯一不知道的就是当前边权的边的数量.
然而在这道题中w的范围给的很小,最大只有10,所以在每次加边的时候我们都可以暴力遍历每种边权的边,然后直接对数量进行修改就行.
具体操作就是:初始化整张图有n个联通块,把给定的图分成10张图,用cnt[i]记录当边权为i时整张图有多少个联通块,每加一条边相当于减少一个联通块.那么在每次加边的时候我们去考虑当前权值以及比当前权值更大的那些权值(小的就不用考虑了,小的权值的连边情况不会变化).如果说在当前权值下当前加的这条边涉及到的两个点原本就是联通的,那么数量就不变;如果两点本来是不联通的,那么将这两点相连并更新联通块数量,开10个并查集去维护图的联通性.
最后cnt[i-1]-cnt[i]代表在权值为i的时候边的数量,用这个性质来计算最小生成树的权值即可.
代码:
struct DSU{
vector<int> fa,siz;
DSU(int n) :fa(n+1),siz(n+1,1){iota(fa.begin(),fa.end(),0);};
int find(int x)
{
while(x!=fa[x]) x=fa[x]=fa[fa[x]];
return x;
}
int size(int x) {return siz[x];}
bool same(int x,int y)
{
return find(x)==find(y);
}
bool merge(int x,int y)
{
x=find(x),y=find(y);
if(x==y)
{
return false;
}
siz[x]+=siz[y];
fa[y]=x;
return true;
}
};
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n,m;
cin>>n>>m;
vector dsu(10,DSU(n));
vector cnt(10,n);
for(int i=1;i<n;i++)
{
int u,v,w;
cin>>u>>v>>w;
for(int j=w;j<=10;j++)
{
cnt[j-1]-=dsu[j-1].merge(u,v);
}
}
while(m--)
{
int u,v,w;
cin>>u>>v>>w;
for(int j=w;j<=10;j++)
{
cnt[j-1]-=dsu[j-1].merge(u,v);
}
int ans=0;
int lst=n;
for(int j=1;j<=10;j++)
{
ans+=j*(lst-cnt[j-1]);
lst=cnt[j-1];
}
cout<<ans<<'\n';
}
}