1. 程式人生 > 其它 >洛谷P2619 [國家集訓隊]Tree I(二分+最小生成樹)

洛谷P2619 [國家集訓隊]Tree I(二分+最小生成樹)

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

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); }
作者:xxy 出處:http://www.cnblogs.com/TheRoadToTheGold/ 本文版權歸作者和部落格園共有,轉載請用連結,請勿原文轉載,Thanks♪(・ω・)ノ。