题目链接
题意:
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
题解:
看的别人的题解。
做法是二分一个权值,可正可负,让所有白色边加上这个权值,然后再做最小生成树,显然这个全权值是可以二分的。
然后最后每次二分得到的结果再加上那些减去的权值就是这种最小生成树的权值和了。
另外权值相同的时候据说需要先选白色边。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,k,f[500010],ans,res;
struct node
{
int x,y,dis,opt;
}a[500010],b[500010];
inline int cmp(node x,node y)
{
if(x.dis==y.dis)
return x.opt<y.opt;
return x.dis<y.dis;
}
inline int getr(int x)
{
if(x==f[x])
return x;
else
{
f[x]=getr(f[x]);
return f[x];
}
}
inline int check(int mid)
{
res=0;
int ji=0,num=0;
for(int i=1;i<=m;++i)
{
b[i]=a[i];
if(a[i].opt==0)
b[i].dis-=mid;
}
sort(b+1,b+m+1,cmp);
for(int i=1;i<=n;++i)
f[i]=i;
for(int i=1;i<=m;++i)
{
int rx=getr(b[i].x),ry=getr(b[i].y);
if(rx!=ry)
{
f[rx]=ry;
res+=b[i].dis;
if(b[i].opt==0)
++ji;
++num;
if(num==n-1)
break;
}
}
res=res+k*mid;
if(ji>=k)
return 1;
else
return 0;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;++i)
{
scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].dis,&a[i].opt);
a[i].x++;
a[i].y++;
}
int l=-100,r=100,mid;
while(l<=r)
{
mid=(l+r)/2;
if(check(mid))
{
r=mid-1;
ans=res;
}
else
l=mid+1;
}
printf("%d\n",ans);
return 0;
}