BZOJ 2654 tree

2654: tree

Time Limit: 30 Sec  Memory Limit: 512 MB
Submit: 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

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;
}

  

 

posted @ 2017-12-22 17:03  zhangenming  阅读(109)  评论(0编辑  收藏  举报