hdu 4253 Two Famous Companies BZOJ 2654 tree

【题意】:给出n个点,m条边,边分为两种,一种是A公司的,一种是B公司的。边上有权值,问用n-1条边把n个点连起来的最小费用是多少,其中A公司的边刚好有k条。题目保证有解。

思路:我们发现,如果我们给白边增加权值,做最小生成树,由于白边权值增大,导致不容易选白边。记f(x)为给白边增加x权值,做最小生成树后,选白边的数量,可以发现,f(x)随x增大而减小。所以可以二分x

首先,直接做MST的话白色边的数量是无法估计的。可能比要求的多,也可能更少

所以考虑怎样调整白色边的数量
通过这个思路,可以想到如果把所有白色边的权值加上/减去一个Δ,那么不考虑答案正确性,可以保证这时候MST跑出来之后白色边的数量一定会增加/减少
那么我们就可以直接二分一个值,使得白边的数量符合要求。
事实上可以证明当我们限定白边的数量一定的时候,MST的答案也是唯一的
那么我们在白边上加上Δ之后算出来MST的答案会多出need*Δ,直接减掉就好了
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 using namespace std;
  6 struct node
  7 {
  8     int v1,v2;
  9     int w;
 10 } s[100005],t[100005];
 11 int n,m,k,cnts,cntt;
 12 int father[100005],sum;
 13 int cmp(const node a,const node b)
 14 {
 15    return a.w<b.w;
 16 }
 17 int find(int x)
 18 {
 19     int i=x,root;
 20     while(x!=father[x])
 21         x=father[x];
 22     root=x;
 23     x=i;
 24     while(x!=father[x])
 25     {
 26         i=father[x];
 27         father[x]=root;
 28         x=i;
 29     }
 30     return root;
 31 }
 32 int deal(int x)
 33 {
 34     for(int i=0; i<=n; i++)
 35         father[i]=i;
 36     int lens=0,lent=0,pp=0;
 37     sum=0;
 38     while(lens<cnts||lent<cntt)
 39     {
 40 
 41         if(s[lens].w+x<=t[lent].w)
 42         {
 43             int s1=find(s[lens].v1);
 44             int s2=find(s[lens].v2);
 45             if(s1!=s2)
 46             {
 47                 father[s1]=s2;
 48                 sum+=s[lens].w+x;
 49                 pp++;
 50             }
 51             lens++;
 52         }
 53         else
 54         {
 55             int s1=find(t[lent].v1);
 56             int s2=find(t[lent].v2);
 57             if(s1!=s2)
 58             {
 59                 father[s1]=s2;
 60                 sum+=t[lent].w;
 61             }
 62             lent++;
 63         }
 64 
 65     }
 66     if(pp>=k)   return 1;
 67     else    return 0;
 68 }
 69 int main()
 70 {
 71     int text=0;
 72     while(scanf("%d%d%d",&n,&m,&k)>0)
 73     {
 74         cnts=0,cntt=0;
 75         for(int i=0; i<m; i++)
 76         {
 77             int v1,v2,w,tmp;
 78             scanf("%d%d%d%d",&v1,&v2,&w,&tmp);
 79             if(tmp==0)
 80             {
 81                 s[cnts].v1=v1;
 82                 s[cnts].v2=v2;
 83                 s[cnts].w=w;
 84                 cnts++;
 85             }
 86             if(tmp==1)
 87             {
 88                 t[cntt].v1=v1;
 89                 t[cntt].v2=v2;
 90                 t[cntt].w=w;
 91                 cntt++;
 92             }
 93         }
 94         sort(s,s+cnts,cmp);
 95         sort(t,t+cntt,cmp);
 96         t[cntt].w=s[cnts].w=(1<<29);
 97         printf("Case %d: ",++text);
 98         int l=-1000,r=1000;
 99         int ans=0;
100         while(l<=r)
101         {
102             //sum=0;
103             int mid=(l+r)/2;
104             if(deal(mid))
105             {
106                 l=mid+1;
107                 ans=sum-mid*k;
108             }
109             else r=mid-1;
110         }
111         printf("%d
",ans);
112     }
113     return 0;
114 }
原文地址:https://www.cnblogs.com/tsw123/p/4440002.html