题目
给你一张图,n个点m条边,
以下m条无向边ui,vi,wi
你可以令某条边权值加1,视为一次操作,
问,令最小生成树唯一时,最少操作数是多少
思路来源
https://www.cnblogs.com/shuaihui520/p/10320158.html
题解
考虑Kruskal加边过程,
一条边在合并的时候,u和v已然在一个集合,加入这条边会成环,不取
而剩下的边,加入就不会成环,说明可以被加入MST,统计一下权值相同的这些边的数量num
然后再遍历一下这些权值相同的边,
注意到之所以会出现最小生成树,是因为使现有树和一些点联通有多种方式,但其拓展的点集是一样的
所以,这些边中碰到一条可以加入MST的边就加入,那么和那个点就联通了,num--
剩下的权值相同的原属于num里的边,此刻再加入就成环了
最后num数就是,原本可以加入MST,但因一些和它相同权值的边的加入,最终未能加入MST的边
这道题的操作是对这些边的边权w+1,
会不会和原本就是w+1的边再冲突呢?
我们注意到,由于选了一些w的边,
使得这些被+1的边再被选的话一定会成环,
所以,不会影响w+1的MST边。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=2e5+10;
struct edge{int u,v,w;}e[maxn];
bool operator<(edge a,edge b)
{
return a.w<b.w;
}
int n,m,par[maxn],ans;
int find(int x)
{
return par[x]==x?x:par[x]=find(par[x]);
}
int main()
{
for(int i=1;i<maxn;++i)par[i]=i;
scanf("%d%d",&n,&m);
for(int i=0;i<m;++i)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
sort(e,e+m);
for(int i=0;i<m;)
{
int j=i;
while(j+1<m&&e[j+1].w==e[i].w)j++;
int tmp=0;
for(int k=i;k<=j;++k)
if(find(e[k].u)!=find(e[k].v))tmp++;
for(int k=i;k<=j;++k)
{
int uu=find(e[k].u),vv=find(e[k].v);
if(uu!=vv)
{
if(uu>vv)par[uu]=vv;
else par[vv]=uu;
tmp--;
}
}
ans+=tmp;
i=j+1;
}
printf("%d\n",ans);
return 0;
}