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\n",ans); 112 } 113 return 0; 114 }