P2619 [国家集训队]Tree I(二分+Kruscal)

题目传送门

题意

给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有 needneed 条白色边的生成树。
题目保证有解。

输入格式

第一行 V,E,need 分别表示点数,边数和需要的白色边数。

接下来 E 行,每行 s,t,c,col 表示这边的端点(点从 0 开始标号),边权,颜色(0 白色 1 黑色)。

输出格式

一行,表示所求生成树的边权和。

样例

input

2 2 1
0 1 1 1
0 1 2 0

output

2

思路

Kruscal求最小生成树,是通过边权从小到大构成MST,那么很显然如果边权越小,那么这条边就越容易被用到,如果我们手动给所有白边都加上一个权值x,那么新的MST中白边的数量显然会跟着x的单调递增而单调递减。于是,我们二分答案这个x就行了。

code

#include <bits/stdc++.h>
using  namespace  std;

typedef long long ll;
typedef unsigned long long ull;
//#pragma GCC optimize(3)
#define pb push_back
#define is insert
#define PII pair<int,int>
#define show(x) cerr<<#x<<" : "<<x<<endl;
//mt19937 mt19937random(std::chrono::system_clock::now().time_since_epoch().count());
//ll getRandom(ll l,ll r){return uniform_int_distribution<ll>(l,r)(mt19937random);}

const ll INF=0x3f3f3f3f;//2147483647;
const int N=5e4+50,M=1e5+50;
const ll mod=998244353;

int n,m,k;
struct node {
    int u,v,val,col;
}e[M],tmp[M];
bool cmp(node a,node b){
    if(a.val==b.val){
        return a.col<b.col;
    }
    return a.val<b.val;
}
int fa[N];
int find(int x){
    return fa[x]==x?x:fa[x]=find(fa[x]);
}

void init(){
    for(int i=0;i<=n;i++){
        fa[i]=i;
    }
}
int ans=INF;
int ans1=INF;
int check(int x){
    //cout<<x<<"\n";
    init();
    for(int i=1;i<=m;i++){
        tmp[i]=e[i];
        if(tmp[i].col==0){
            tmp[i].val+=x;
        }
    }
    sort(tmp+1,tmp+1+m,cmp);
    int res=0;
    int tot=0;
    for(int i=1;i<=m;i++){
        int a=find(tmp[i].u),b=find(tmp[i].v);
        if(a!=b){
            if(tmp[i].col==0){
                tot++;
            }
            fa[a]=b;
            res+=tmp[i].val;
        }
    }
    //cout<<res<<" "<<tot<<"\n";
    if(tot==k){
        ans1=min(ans1,res-k*x);
    }
    if(tot>k){
        ans=res-k*x;
        //ans=min(ans,res-k*x);
        return 1;
    }
    return 0;
}
void solve() {
    cin>>n>>m>>k;
    for(int i=1;i<=m;i++){
        cin>>e[i].u>>e[i].v>>e[i].val>>e[i].col;
    }
    int l=-110,r=110;
    while(l<r){
        int mid=l+r>>1;
        if(check(mid)){
            l=mid+1;
        }
        else r=mid;
    }
    cout<<min(ans1,ans);
}

signed main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int __=1;//cin>>__;
    while(__--){
        solve();
    }
    return 0;
}
posted @ 2022-03-31 11:17  illume  阅读(44)  评论(0编辑  收藏  举报