洛谷P2619 [國家集訓隊]Tree I(二分+最小生成樹)
阿新 • • 發佈:2021-08-12
https://www.luogu.com.cn/problem/P2619
邊有黑白兩色,求恰好有k條白邊的最小生成樹
在克魯斯卡爾演算法中,將邊權從小到大排序
我們可以通過將白邊的權值加減,來改變白邊在排序中的位置
假設要求白邊用5條,現在是所有白邊權值加3
若求出來白邊用了8條,說明權值加小了,白邊在前面太多,需要加更多的權值,把一些白邊扔到後面
若求出來白邊用了2條,說明權值加大了,前面的白邊太少,需要加小些的權值,把一些白邊扔到前面
這樣就可以二分進行加權值的調整
若找到了一個權值,加上之後,白邊恰好用了要求的條數,皆大歡喜
若加權值x,白邊使用條數大於要求條數;然而加權值x+1,導致白邊使用條數小於要求條數
這種情況下,存在黑邊與加權之後的白邊權值相同,這樣把白邊換成黑邊即可
所以二分過程中,只要使用條數大於等於要求條數,就更新一次答案
#include<bits/stdc++.h> using namespace std; #define N 50001 #define M 100001 int n,m,k; struct node { int u,v,w,c; }e[M]; int use,sum; int fa[N]; bool cmp(node p,node q) { if(p.w!=q.w) return p.w<q.w; return p.c<q.c; }作者:xxy 出處:http://www.cnblogs.com/TheRoadToTheGold/ 本文版權歸作者和部落格園共有,轉載請用連結,請勿原文轉載,Thanks♪(・ω・)ノ。int find(int i) { return fa[i]==i ? i : fa[i]=find(fa[i]); } void check(int x) { for(int i=1;i<=m;++i) if(!e[i].c) e[i].w+=x; sort(e+1,e+m+1,cmp); for(int i=1;i<=n;++i) fa[i]=i; int now=0,fu,fv,j=1; use=sum=0; while(now!=n-1) { fu=find(e[j].u); fv=find(e[j].v); if(fu!=fv) { now++; fa[fu]=fv; if(!e[j].c) ++use; sum+=e[j].w; } ++j; } for(int i=1;i<=m;++i) if(!e[i].c) e[i].w-=x; } int main() { scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=m;++i) { scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].w,&e[i].c); ++e[i].u; ++e[i].v; } int l=-101,r=101,mid,ans; while(l<=r) { mid=l+r>>1; // printf("%d ",mid); check(mid); if(use>=k) { // printf("\n"); ans=sum-k*mid; l=mid+1; } else r=mid-1; } printf("%d",ans); }