容易想到二分。
二分一个值w,将与1相连的边全部减去w,之后做mst。
那么w越大,mst选择的与1相连的边的个数(记作num)就越大。
那么二分出使得num=k的w即可。
因为我们保证了结果是原mst-k*w后最小的,
所以也一定保证了原mst最小。
但问题是,对于一个w,num的值是不确定的,因为最小生成树有很多选择。
网上有人说把二分的w改成double即可,我认为这是错的。
double其实没什么意义,由于边权是整数,1.1和1.2做出来的mst一定是一样的。
其实对于一个w,num的值应该是一个区间[l,r]。
我们二分出使得r>=k的最小的w,那么[l,r]一定包含k,因为w-1的r<k,
这时我们再做一遍mst,使得恰好选k条边即可。
#include<bits/stdc++.h>
using std::sort;
const int N=5005,M=100000,U=100001;
int n,m,k,i;
struct edge
{
int x,y,id,w;
bool is;
bool operator <(const edge &i)const
{
if(w!=i.w) return w<i.w;
return is;
}
}q[M+3];
int st[N],top;
int f[N];
int find(int x)
{
return x==f[x]?x:f[x]=find(f[x]);
}
int check(int w,bool can)
{
for(i=1;i<=m;++i)
if(q[i].is)q[i].w-=w;
int ans=0,x,y;
sort(q+1,q+m+1);
for(i=1;i<=n;++i) f[i]=i;
for(top=0,i=1;i<=m;++i)
{
x=find(q[i].x);y=find(q[i].y);
if(x==y) continue;
if(!can&&ans+q[i].is>k) continue;
f[x]=y;
st[++top]=q[i].id;
ans+=q[i].is;
}
for(i=1;i<=m;++i)
if(q[i].is)q[i].w+=w;
return ans;
}
#define mid (l+r>>1)
int main()
{ freopen("1.in","r",stdin);
scanf("%d%d%d",&n,&m,&k);
for(i=1;i<=m;++i)
{
scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].w);q[i].id=i;
q[i].is=(q[i].x==1)||(q[i].y==1);
}
if(check(U,1)<k||check(-U,1)>k||top<n-1) printf("-1\n");
else
{
int l=-U,r=U;
while(l+1!=r)
if(check(mid,1)<k) l=mid;
else r=mid;
check(r,0);
printf("%d\n",n-1);
for(;top;--top) printf("%d ",st[top]);
}
}