试题链接:https://ac.nowcoder.com/acm/contest/1221/H
/*
思路:
(1)将边从大到小排序
(2)对于相同权值的边统一考虑,若这条边上两点不连通开始全部加入ans中,然后再考虑重复加入的情况,也是逐渐加入这些权值相同的边,若两点不连通
ans-=w;否则说明这条边不唯一,应该删去
要形成最小生成树,那么就没有别的边可以在构造最小生成树时加入最小生成树
(对于相同权值的边统一考虑,若这条边上两点不连通开始全部加入ans中,然后再考虑重复加入的情况,
也是逐渐加入这些权值相同的边
若两点不连通ans-=w,否则说明这条边不唯一,应该删去)
我们可以在计算最小生成树时,求所有可以加入最小生成树的边权和,再减去一个最小生成树的边权和就是答案
*/
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <sstream>
#include <map>
#include <set>
#include <queue>
#include <stdlib.h>
typedef long long ll;
using namespace std;
const int MaxN = 200005;
struct edge
{
int u, v, w;
}E[MaxN];
bool cmp(edge x ,edge y) //下面要把每条边按照权值排序,就是一个结构体b排序,可以直接在sort里面加cmp,就不用写重载操作函数了
{
return x.w<y.w;
}
int N, M;
long long Ans;
int Pre[MaxN];
void init() //初始化
{
cin >> N >> M;//图的点数和边数
for (int i = 1; i <= M; ++i)
{
cin >> E[i].u >> E[i].v >> E[i].w; //u,v表示两个端点,w表示权值(定义删除一条边的代价为w)
}
}
int find(int i)
{
if(i==Pre[i]) return i;
else return Pre[i]=find(Pre[i]);
}
void solve()
{
//M为边数,N为点数
sort(E+1,E+1+M,cmp); //M为边数
for (int i = 1; i <= N; ++i) //各自为自己的祖先
{
Pre[i] = i;
}
for (int i = 1; i <= M; ++i) //M次循环,M条边,i表示边数
{
//如果 前一条边 跟 后一条边 的值相等,就直接continue
if (i == M || E[i].w != E[i + 1].w) //如果i是最后一条边 或者 第i条边的值不等于第i+1条边的值
{
for (int j = i; j>=1 && E[j].w == E[i].w; j--) //从那个当前这条边不断往上找
{
int u=E[j].u;
int v=E[j].v;
int w=E[j].w;
int fu=find(u);
int fv=find(v);
//如果两条边在一个连通分量中,continue
if(fv==fu) continue; //如果两条边不在一个连通分量中,答案加上E[i].w(先把所有权值相等的边加到ans中)
Ans += w; //在这里不用将边的两点的祖先连在一起
}
for (int j = i; j>=1&&E[j].w==E[i].w ; j--)
{
int u=E[j].u;
int v=E[j].v;
int fu=find(u);
int fv=find(v);
if(fu==fv) continue;
Pre[find(u)] = find(v); //将两者连在一起
Ans -= E[i].w;
}
}
}
cout << Ans << endl;
}
int main()
{
init();
solve();
return 0;
}