试题链接: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; }