传送门
【题目分析】
总感觉题目似曾相识的样子qwq。
其实二分的思路还是很好想的,初始状态无非就是在生成树中的白边的数量>需要的数量和<需要的数量,前者需要减少白边数量加黑边,后者相反。
减少白边相当于将白边的权值整体加一个值,增加就相当于减少一个值,就这样做最小生成树,最后因为恰好是need条白边所以权值一定就是(生成树权值-need*整体加的值)
【代码~】
#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e4+10;
const int MAXM=1e5+10;
int n,m,k;
int fa[MAXN],ans;
int val;
struct Edge{
int u,v,w;
int col;
friend inline bool operator<(const Edge &a,const Edge &b){
if(a.w==b.w)
return a.col<b.col;
return a.w<b.w;
}
}edge[MAXM];
int Read(){
int i=0,f=1;
char c;
for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')
f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())
i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
int find(int x){
if(x==fa[x])
return x;
return fa[x]=find(fa[x]);
}
bool check(int x){
int sum=0,tot=0;
val=0;
for(int i=1;i<=m;++i)
if(edge[i].col==0)
edge[i].w+=x;
for(int i=1;i<=n;++i)
fa[i]=i;
sort(edge+1,edge+m+1);
for(int i=1;i<=m;++i){
int u=edge[i].u,v=edge[i].v;
int fu=find(u),fv=find(v);
if(fu!=fv){
sum++;
val+=edge[i].w;
fa[fu]=fv;
if(edge[i].col==0)
tot++;
if(sum==n-1){
return tot>=k;
}
}
}
return 0;
}
int main(){
n=Read(),m=Read(),k=Read();
for(int i=1;i<=m;++i){
edge[i].u=Read()+1;
edge[i].v=Read()+1;
edge[i].w=Read();
edge[i].col=Read();
}
int l=-100,r=100,mid;
while(l<=r){
mid=l+r>>1;
if(check(mid))
l=mid+1,ans=val-k*mid;
else
r=mid-1;
for(int i=1;i<=m;++i)
if(edge[i].col==0)
edge[i].w-=mid;
}
cout<<ans;
return 0;
}