洛谷.2619.[国家集训队2]Tree I(带权二分 Kruskal)

本文介绍了一种使用Kruskal算法结合二分搜索策略求解给定图中包含特定数量白色边的最小生成树的方法。通过为白色边设置额外权重并调整权重值,确保生成树既最小又能满足白边数量限制。

题目链接

\(Description\)

给定一个无向带权连通图,每条边是黑色或白色。求一棵最小权的恰好有K条白边的生成树。

\(Solution\)

Kruskal是选取最小的n-1条边。而白边数有限制,考虑为其设额外边权C。
随着C增大,选的白边数应是不增的。可以二分求一个C值使得此时恰好选择K条边。选取时应优先选白边,因为多了还可以用黑边替换,少了只能减少C。
可能的问题是,C=mid时白边数>K,C=mid+1时白边数<K,因为有很多黑边与+mid后的白边权值想等。可以用这些黑边替换掉多余的白边,所以答案应是减K*mid(保留黑边)。

//404ms 3.36MB
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 100000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=5e4+5,M=1e5+5;

int n,m,K,fa[N],Ans;
char IN[MAXIN],*SS=IN,*TT=IN;
int read();
struct Edge
{
    int fr,to,val,col;
    inline void Init(){
        fr=read()+1, to=read()+1, val=read(), col=read();
    }
    bool operator <(const Edge &x)const{
        return val==x.val?col<x.col:val<x.val;
    }
}e[M];

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now;
}
int GetFa(int x){
    return x==fa[x]?x:fa[x]=GetFa(fa[x]);
}
bool Check(int x)
{
    for(int i=1; i<=m; ++i) if(!e[i].col) e[i].val+=x;
    for(int i=1; i<=n; ++i) fa[i]=i;
    std::sort(e+1,e+1+m);
    int sum=0, cnt=0;
    for(int i=1,k=0,r1,r2; i<=m; ++i)
    {
        if((r1=GetFa(e[i].fr))==(r2=GetFa(e[i].to))) continue;
        fa[r1]=r2, sum+=e[i].val;
        cnt+=e[i].col^1;
        if(++k+1==n) break;
    }
    for(int i=1; i<=m; ++i) if(!e[i].col) e[i].val-=x;
    if(cnt<K) return 0;
    Ans = sum-K*x;//不能取min!
    return 1;
}

int main()
{
    n=read(),m=read(),K=read();
    for(int i=1; i<=m; ++i) e[i].Init();
    Ans=m*101; int l=-101,r=101,mid;
    while(l<=r)//l==r时要Check一遍(或者结束时)
        if(Check(mid=l+r>>1)) l=mid+1;
        else r=mid-1;
//  Check(l);
    printf("%d",Ans);

    return 0;
}

转载于:https://www.cnblogs.com/SovietPower/p/9162351.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值