[NC53074]Forsaken喜欢独一无二的树

一、题目

NC53074
[NC53074]Forsaken喜欢独一无二的树

二、思路

题目大意就是一棵最小生成树可能有多种不同的组成方式,我们要去掉哪些边才能使整个图只有一种最小生成树,并对去掉的这些边求和

首先一棵树如果有多种不同的组成方式,在kruskal的方法下,只有可能是在选边的时候出现了多条相同边权的边连接着两个已经连接上的子树
所以直接对kruskal进行修改即可,在选边时遍历相同边权的边,先将未连接的边的权值全加起来,再进行连边,因为连上的边不是重复的边,所以我们的答案应该再减去连上的边的权值

三、代码

#include<bits/stdc++.h>
using namespace std;
const int N = 400020;
int n, m;
int s[N];
long long ans = 0; //注意开long long,不然可能爆int
struct Edge{
	int u, v;
	int w;
	bool operator < (const Edge & b) const{
		return w < b.w;
	}
}edges[N];
void init(){
	for(int i = 0; i <= n; i ++) s[i] = i;
}
int find(int x){
	if(s[x] != x) return s[x] = find(s[x]);
	return x;
}
void kruskal(){
	sort(edges + 1, edges + 1 + m);
	int j = 1;
	for(int i = 1; i <= m; i){ //这里直接用while更好,i注意不会自增
		while(edges[i].w == edges[j].w && j <= m) j ++; //j一直自增到下一个不同边权的位置
		for(int z = i; z < j; z ++){
			int u = edges[z].u;
			int v = edges[z].v;
			if(find(u) != find(v)){
				ans += edges[z].w;
			}
		}
		for(int z = i; z < j; z ++){
			int u = edges[z].u;
			int v = edges[z].v;
			if(find(u) != find(v)){
				ans -= edges[z].w;
				s[find(u)] = find(v);
			}
		}
		i = j; //因为前j条边已经遍历完了,所以i直接到j的位置即可
	}
}
int main(){
	cin >> n >> m;
	for(int i = 1; i <= m; i ++){
		cin >> edges[i].u >> edges[i].v >> edges[i].w;
	}
	init();
	kruskal();
	cout << ans << endl;
}
上一篇:Kruskal模板——并查集实现


下一篇:CF1385E Directing Edges 拓扑序