BZOJ 2654: tree

Description

\(n\) 个点, \(m\) 条边,边有权值和黑/白色,求含有 \(need\) 个白边的生成树.

Sol

二分+Kruskal.

将每条白边都加上一个权值,然后跑最小生成树.

二分这个权值,显然随着权值的增加,白边个数减少,让权值尽可能大.

最后再减去权值即可.

Code

/**************************************************************
    Problem: 2654
    User: BeiYu
    Language: C++
    Result: Accepted
    Time:1356 ms
    Memory:4612 kb
****************************************************************/
 
#include<cstdio>
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
  
const int N = 50005;
const int M = 100005;
#define mid ((l+r)>>1)
#define debug(x) cout<<#x<<"="<<x<<" " 
  
struct Edge{ int fr,to,v,c; }edge[M],tmp[M];
bool operator < (const Edge &a,const Edge &b){ return a.v == b.v ? a.c < b.c : a.v < b.v; }
  
int n,m,nd,ans;
int f[N];
  
inline int in(int x=0,char ch=getchar()){ while(ch>'9' || ch<'0') ch=getchar();
    while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x; }
int find(int x){ return f[x] == x ? x :f[x] = find(f[x]); }
int Kruskal(int x){
    for(int i=1;i<=m;i++){ tmp[i]=edge[i];if(!tmp[i].c) tmp[i].v+=x; }
    sort(tmp+1,tmp+m+1);
    for(int i=1;i<=n;i++) f[i]=i;
    int cost=0,cnt=1,cw=0;
    for(int i=1;i<=m;i++){
        int fr=tmp[i].fr,to=tmp[i].to,v=tmp[i].v,c=tmp[i].c;
        if(find(fr)!=find(to)) f[find(fr)]=find(to),cost+=v,cw+=1-c,cnt++;
        if(cnt==n) break;
    }return ans=cost-x*nd,cw;
}
int main(){
    n=in(),m=in(),nd=in();
    for(int i=1,a,b,c,d;i<=m;i++) a=in()+1,b=in()+1,c=in(),d=in(),edge[i]=(Edge){ a,b,c,d };
    int l=-100,r=100;
    while(l<=r){
        if(Kruskal(mid)>=nd) l=mid+1;
        else r=mid-1;
    }Kruskal(r),printf("%d\n",ans);
    return 0;
}

  

posted @ 2016-10-25 20:16  北北北北屿  阅读(146)  评论(0编辑  收藏  举报