题意
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
题目保证有解。
解析
这题直接考虑白边不好做,不过我们可以给白边加上一个值x,而x越大,选取的白边就越少,x越小,选取的白边就越多。所以可以二分x,找到符合条件的解。
#include <cstdio>
#include <algorithm>
#define Rep( i , _begin , _end ) for(int i=(_begin);i<=(_end);i++)
#define For( i , _begin , _end ) for(int i=(_begin);i!=(_end);i++)
using std :: sort;
const int maxx = 100000 + 25;
int ftr[maxx],u[maxx],v[maxx],w[maxx],c[maxx];
int n,m,x,y,z,ans,need,L,R,cnt,tot,now;
namespace mst{
struct Edges{
int u,v;
int c,w;
}E[maxx];
bool cmp(Edges a,Edges b){
return a.w == b.w? a.c<b.c : a.w<b.w;
}
int find(int x){
return ftr[x] == x? x : ftr[x] = find(ftr[x]);
}
}
using namespace mst;
bool check(int x){
cnt = 0,tot = 0,now = 0;
Rep( i , 1 , n ) ftr[i] = i;
Rep( i , 1 , m ){
E[i].u=u[i],E[i].v=v[i],E[i].w=w[i],E[i].c=c[i];
if(E[i].c == 0) E[i].w += x;
}
sort(E+1,E+m+1,cmp);
Rep( i , 1 , m ){
int f = find(E[i].u),t = find(E[i].v);
if(f != t){
cnt++,ftr[t] = f,tot += E[i].w;
if(E[i].c == 0) now ++;
}
if(cnt == n-1) break;
}
return now >= need;
}
int main(){
scanf("%d%d%d",&n,&m,&need);
Rep( i , 1 , m ) scanf("%d%d%d%d",&u[i],&v[i],&w[i],&c[i]),u[i]++,v[i]++;
L = -125;R = 125;
while(L <= R){
int mid = (L+R) >> 1;
if(check(mid)) L = mid + 1,ans = tot - need*mid;
else R = mid - 1;
}
printf("%d",ans);
return 0;
}