(MST+二分) bzoj 2654
2654: tree
Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 515 Solved: 195
[Submit][Status][Discuss]
Description
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
题目保证有解。
Input
第一行V,E,need分别表示点数,边数和需要的白色边数。
接下来E行
每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。
Output
一行表示所求生成树的边权和。
Sample Input
2 2 1
0 1 1 1
0 1 2 0
0 1 1 1
0 1 2 0
Sample Output
2
HINT
数据规模和约定
0:V<=10
1,2,3:V<=15
0,..,19:V<=50000,E<=100000
所有数据边权为[1,100]中的正整数。
Source
脑洞有点大,我们把白色的权值加上某个值,那么白色取到的边的数目会减少,具有单调性,可以二分。
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<algorithm> #include<cstdlib> using namespace std; int V,E,need,tot,sum; struct node { int x,y,w,col; }e[100005],a[100005]; int fa[100005]; bool cmp(node aa,node bb) { if(aa.w==bb.w) return aa.col>bb.col; return aa.w<bb.w; } int find(int x) { if(x!=fa[x]) fa[x]=find(fa[x]); return fa[x]; } void check(int mid) { tot=0,sum=0; for(int i=0;i<=V;i++) fa[i]=i; for(int i=1;i<=E;i++) { e[i].x=a[i].x; e[i].y=a[i].y; e[i].w=a[i].w+a[i].col*mid; e[i].col=a[i].col; } sort(e+1,e+1+E,cmp); for(int i=1;i<=E;i++) { int fx,fy; fx=find(e[i].x),fy=find(e[i].y); if(fx!=fy) { if(e[i].col) tot++; sum+=e[i].w; fa[fx]=fy; } } } int main() { int ans=0; scanf("%d%d%d",&V,&E,&need); for(int i=1;i<=E;i++) { scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].w,&a[i].col); a[i].col^=1; } int l,r,mid; l=-105,r=105; while(l<=r) { mid=(l+r)>>1; check(mid); if(tot>=need) ans=sum-need*mid,l=mid+1; else r=mid-1; } printf("%d\n",ans); return 0; }