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 }