bzoj 2654 tree

题目大意:

一个无向带权图,每条边为黑或白(1/0)

求包含k条白边的最小生成树,保证有解

思路:

蛇皮题

高老师提醒了我们二分

就是二分+mst

我们二分一个权值,然后对于每个白边加上这个权值

这样就可以算出我们要取哪些白边

所以二分的范围要到负数

如果知道要取哪些白边和黑边,然后直接求出这个树的权值减去白边被加上的值

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cmath>
 5 #include<cstring>
 6 #include<cstdlib>
 7 #include<set>
 8 #include<map>
 9 #include<vector>
10 #include<stack>
11 #include<queue>
12 #define ll long long
13 #define inf 2147383611
14 #define MAXN 50100
15 using namespace std;
16 inline ll read()
17 {
18     ll x=0,f=1;
19     char ch;ch=getchar();
20     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
21     while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
22     return x*f;
23 }
24 int n,m,k;
25 struct data
26 {
27     int from,to,val,col;
28     bool operator < (const data &a) const
29     {
30         return val<a.val;
31     }
32 }edge[MAXN*2],tmp[MAXN*2];
33 int f[MAXN],ans,res;
34 int find(int x) {return x==f[x]?x:f[x]=find(f[x]);}
35 bool ok(int a,int b)
36 {
37     int fa=find(a),fb=find(b);
38     if(fa==fb) return 1;
39     else {f[fa]=fb;return 0;}
40 }
41 bool check(int x)
42 {
43     int cnt=0;res=0,ans=0;
44     for(int i=1;i<=n;i++) f[i]=i;
45     for(int i=1;i<=m;i++) {tmp[i]=edge[i];if(!tmp[i].col) tmp[i].val+=x;}
46     sort(tmp+1,tmp+m+1);
47     for(int i=1;i<=m;i++)
48     {
49         if(!ok(tmp[i].from,tmp[i].to)) {cnt++,ans+=tmp[i].val;if(!tmp[i].col) {res++;ans-=x;}}
50         if(cnt==n-1) break;
51         //cout<<x<<" "<<cnt<<" "<<ans<<endl;
52     }
53     //cout<<x<<" "<<res<<" "<<ans<<endl;
54     return res>=k;
55 }
56 int main()
57 {
58     n=read(),m=read(),k=read();
59     for(int i=1;i<=m;i++) edge[i].from=read()+1,edge[i].to=read()+1,edge[i].val=read(),edge[i].col=read();
60     int l=-101,r=101,mid;
61     while(l<r-1)
62     {
63         mid=(l+r)>>1;
64         if(check(mid)) l=mid;
65         else r=mid;
66         if(res==k) break;
67     }
68     printf("%d",ans);
69 }
View Code
posted @ 2017-10-23 17:19  jack_yyc  阅读(212)  评论(0编辑  收藏  举报