BZOJ 2654 tree
2654: tree
Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 2600 Solved: 1069
[Submit][Status][Discuss]
Description
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
题目保证有解。
Input
第一行V,E,need分别表示点数,边数和需要的白色边数。
接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。
Output
一行表示所求生成树的边权和。
V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。
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
原数据出错,现已更新 by liutian,但未重测---2016.6.24
Source
二分+kruskal
给白色边都加上一个值,做mst会使得选取的白边数量减少,所以可以二分它
#include <bits/stdc++.h> using namespace std; inline int read() { int x=0;int f=1;char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} return x*f; } const int MAXN=1e6+10; int fa[MAXN],ans,n,m,need,xx[MAXN],yy[MAXN],vv[MAXN],flag[MAXN],cnt,tot; struct node{ int x,y,v,flag; }e[MAXN]; int getfather(int st){ return fa[st]==st?st:fa[st]=getfather(fa[st]); } bool mycmp(node n,node m){ return n.v<m.v||(n.v==m.v&&n.flag<m.flag); } void init(){ n=read();m=read();need=read(); for(int i=1;i<=m;i++){ xx[i]=read();yy[i]=read();vv[i]=read();flag[i]=read(); } } bool check(int x){ cnt=0; tot=0; for(int i=0;i<n;i++){ fa[i]=i; } for(int i=1;i<=m;i++){ e[i].x=xx[i];e[i].y=yy[i];e[i].v=vv[i];e[i].flag=flag[i]; if(!flag[i]) e[i].v+=x; } sort(e+1,e+m+1,mycmp); for(int i=1;i<=m;i++){ int xx=e[i].x;int yy=e[i].y; int tx=getfather(xx);int ty=getfather(yy); if(tx!=ty) {fa[tx]=ty;if(!e[i].flag) cnt++;tot+=e[i].v;} } return cnt>=need; } void solve(){ int l=-105;int r=105; int ans; while(l<=r){ int mid=(r+l)>>1; if(check(mid)) ans=tot-cnt*mid,l=mid+1; else r=mid-1; } cout<<ans<<endl; } int main(){ init(); solve(); return 0; }