Luogu2619[国家集训队2] Tree I

39 篇文章 0 订阅
15 篇文章 0 订阅

原题链接:https://www.luogu.org/problemnew/show/P2619

Tree I

题目描述

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

题目保证有解。

输入输出格式
输入格式:

第一行V,E,need分别表示点数,边数和需要的白色边数。

接下来E行

每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。

输出格式:

一行表示所求生成树的边权和。

输入输出样例
输入样例#1:

2 2 1
0 1 1 1
0 1 2 0

输出样例#1:

2

说明

0:V<=10

1,2,3:V<=15

0,…,19:V<=50000,E<=100000

所有数据边权为[1,100]中的正整数。

By WJMZBMR

题解

带权二分第一题。。。

如果白色边的权值都加上 + ∞ +\infty +,最小生成树中的白色边会尽量少;如果白色边权值都加上 − ∞ -\infty ,最小生成树中的白色边就会尽量多。

所以我们二分白色边的附加权值,每次做 K r u s c a l Kruscal Kruscal,看白边有几条,最后输出答案即可。

代码
#include<bits/stdc++.h>
#define inf 100
using namespace std;
const int M=1e5+5;
struct sd{int a,b,val,col;}ed[M],now[M];
bool operator<(sd a,sd b){return a.val==b.val?a.col<b.col:a.val<b.val;}
int f[M],n,m,need,cot,tot,ans;
int root(int v){return f[v]==v?v:f[v]=root(f[v]);}
bool check(int d)
{
    ans=cot=tot=0;
    for(int i=1;i<=n;++i)f[i]=i;
    for(int i=1;i<=m;++i)now[i]=ed[i],now[i].val+=(ed[i].col?0:d);
    sort(now+1,now+1+m);
    for(int i=1;i<=m&&tot<n;++i)if(root(now[i].a)!=root(now[i].b))
    f[f[now[i].a]]=f[now[i].b],cot+=(now[i].col^1),ans+=now[i].val,++tot;
    return cot>=need;
}
void in()
{
    scanf("%d%d%d",&n,&m,&need);
    for(int i=1,a,b,c,d;i<=m;++i)
    scanf("%d%d%d%d",&a,&b,&c,&d),ed[i]=(sd){a+1,b+1,c,d};
}
void ac()
{
    int l=-inf,r=inf,mid,d;
    for(;l<=r;mid=l+r>>1,check(mid)?(l=mid+1,d=mid):r=mid-1);
    check(d);printf("%d",ans-need*d);
}
int main(){in(),ac();}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值