P2619 [國家集訓隊]Tree I(二分+Kruscal)
阿新 • • 發佈:2022-03-31
題意
給你一個無向帶權連通圖,每條邊是黑色或白色。讓你求一棵最小權的恰好有 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; }