[bzoj2654]tree
可以发现k条白边的最小生成树可以用:1.求出黑边的最小生成树;2.贪心选择[白边-对应环上最大的黑边]最小的加入;3.重复2操作k次
考虑如何模拟这个过程,由于每一次贪心的代价是单调递增的,所以可以二分确定最后一次代价mid,即每一次贪心都有白边-替换边<=mid
如果将所有白边边权减去mid(记为白边'),则白边'<=替换边,如果求最小生成树那么就会将白边'加入最小生成树中,而这恰好就是贪心的结果
但还有一些细节:一个mid是答案当且仅当能够加入k条白边及以上且尽量小(当然也可以定义为不能够加入k+1条白边及以上且尽量大),根据这个来二分会比较方便
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 50005 4 int n,m,t,ans,f[N]; 5 struct ji{ 6 int x,y,z,w; 7 bool operator < (const ji &a)const{ 8 return (z<a.z)||(z==a.z)&&(w<a.w); 9 } 10 }e[N<<1]; 11 int find(int k){ 12 if (k==f[k])return k; 13 return f[k]=find(f[k]); 14 } 15 bool check(int k){ 16 sort(e+1,e+m+1); 17 int s=0; 18 for(int i=0;i<n;i++)f[i]=i; 19 for(int i=1;i<=m;i++) 20 if (find(e[i].x)!=find(e[i].y)){ 21 ans+=e[i].z; 22 f[find(e[i].x)]=find(e[i].y); 23 if ((!e[i].w)&&(++s>=t))return 1; 24 } 25 return 0; 26 } 27 int main(){ 28 scanf("%d%d%d",&n,&m,&t); 29 for(int i=1;i<=m;i++)scanf("%d%d%d%d",&e[i].x,&e[i].y,&e[i].z,&e[i].w); 30 int l=-200,r=200; 31 while (l<r){ 32 int mid=(l+r>>1); 33 for(int i=1;i<=m;i++) 34 if (!e[i].w)e[i].z-=mid; 35 if (check(mid))r=mid; 36 else l=mid+1; 37 for(int i=1;i<=m;i++) 38 if (!e[i].w)e[i].z+=mid; 39 } 40 ans=0; 41 for(int i=1;i<=m;i++) 42 if (!e[i].w)e[i].z-=l; 43 sort(e+1,e+m+1); 44 for(int i=0;i<n;i++)f[i]=i; 45 for(int i=1;i<=m;i++) 46 if (find(e[i].x)!=find(e[i].y)){ 47 ans+=e[i].z; 48 f[find(e[i].x)]=find(e[i].y); 49 } 50 printf("%d",ans+l*t); 51 }