1. 程式人生 > 其它 >P2619 [國家集訓隊]Tree I(二分+Kruscal)

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