[bzoj2654]tree

可以发现k条白边的最小生成树可以用:1.求出黑边的最小生成树;2.贪心选择[白边-对应环上最大的黑边]最小的加入;3.重复2操作k次
考虑如何模拟这个过程,由于每一次贪心的代价是单调递增的,所以可以二分确定最后一次代价mid,即每一次贪心都有白边-替换边<=mid
如果将所有白边边权减去mid(记为白边'),则白边'<=替换边,如果求最小生成树那么就会将白边'加入最小生成树中,而这恰好就是贪心的结果
但还有一些细节:一个mid是答案当且仅当能够加入k条白边及以上且尽量小(当然也可以定义为不能够加入k+1条白边及以上且尽量大),根据这个来二分会比较方便

 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 }
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/11367398.html