题目分析:
求满足题意的最小生成树的权值->首先要满足要求->选出的最小生成树有need条白边->可是选出的最小生成树不一定有need条白边这么办->对于每一次Kruskal->我们可以得到当前最小生成树中白边的数量Cnt->如果Cnt>need->白边的数量比所需要的多了->白边的权值太小->使每一条白边都加去一个值->使得有可能下一次跑Kruskal统计出来的Cnt减少->如果出现Cnt白边的数量比所需要的少了->白边的权值太大->使每一条白边都减去一个值->使得有可能下一次跑Kruskal统计出来的Cnt增多->我们该如何修改白边的权值->白边的权值修改多少->得解->二分答案+生成树。
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define DB double
#define SG string
#define LL long long
#define Fp(A,B,C,D) for(A=B;A<=C;A+=D)
#define Fm(A,B,C,D) for(A=B;A>=C;A-=D)
#define Clear(A) memset(A,0,sizeof(A))
using namespace std;
const LL Max=1e5+5;
const LL Mod=1e9+7;
const LL Inf=1e18;
struct Node{
LL C,X,Y,V;
void Get(LL A,LL B,LL P,LL Q){
C=A;X=B;Y=P;V=Q;
}
}G[Max<<1];
LL N,M,W,Ans,Cnt,Tot,Left=-105,Mid,Right=105,C[Max<<1],X[Max<<1],Y[Max<<1],V[Max<<1],F[Max<<1];
inline LL Read(){
LL X=0;char CH=getchar();bool F=0;
while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
return F?-X:X;
}
inline void Write(LL X){
if(X<0)X=-X,putchar('-');
if(X>9)Write(X/10);
putchar(X%10+48);
}
bool Cmp(Node A,Node B){
return A.V==B.V?A.C<B.C:A.V<B.V;
}
LL Find(LL X){
return F[X]==X?X:Find(F[X]);
}
void Kruskal(){
LL I,J,K;Ans=Cnt=0;
Fp(I,1,N,1){
F[I]=I;
}
Fp(I,1,M,1){
G[I].Get(C[I],X[I],Y[I],V[I]);
if(!G[I].C){
G[I].V-=Mid;
}
}sort(G+1,G+1+M,Cmp);
Fp(I,1,M,1){
LL P=Find(G[I].X);
LL Q=Find(G[I].Y);
if(P!=Q){
F[P]=Q;
Ans+=G[I].V;
if(!G[I].C){
Cnt++;
}
}
}
}
int main(){
LL I,J,K;
N=Read(),M=Read(),W=Read();
Fp(I,1,M,1){
X[I]=Read()+1;
Y[I]=Read()+1;
V[I]=Read();
C[I]=Read();
}
while(Left<=Right){
Mid=Left+Right>>1;
Kruskal();
if(Cnt>=W){
Right=Mid-1;
Tot=Ans+W*Mid;
} else {
Left=Mid+1;
}
}Write(Tot);
return 0;
}