二分+Kruskal【p2798】爆弹虐场

Kiana面对教学难题,如何在限定条件下,找到连接知识点的最佳路径,确保教学质量和效率。通过二分查找与Kruskal算法结合,求解最小生成树问题。

Description

某年某月某日,Kiana 结识了一名爆弹虐场的少年。

Kiana 仗着自己多学了几年OI,所以还可以勉勉强强给这位少年 讲一些自己擅长的题。具体来说,Kiana 先给这位少年灌输了n 个毫不相干的知识点,然后再通过自己的[数据删除]技术把这些知识点强行联系在一起。

由于这位少年有着爆弹虐场的实力,所以对于每个Kiana 准备强行构造的联系,他都能够自己想出来,不过会花费更多的时间。具体来说,Kiana 一共有m 个联系,每个联系可以把两个不相干的知识点连在一起,如果由Kiana 直接来讲第i 个联系,需要花费ti 的时间, 而如果由少年自己想出来,则需要花费Ti 的时间。

为了偷懒,Kiana 只需要自己讲的或少年想出来的联系能刚好把知识点全部直接或间接串在一起就可以了。但为了保证教学质量, Kiana 觉得至少有k 个联系需要少年自己想出来。由于Kiana 耐心有限,她希望无论是自己讲或是少年自己想,构造的联系中花费时间最长的一个用时最短。

现在Kiana 想知道,满足这些条件的情况下,构造的联系中耗时最长的一个的最短用时是多少。由于她不会算,所以希望由你告诉她。

Input

输入文件包括m+1 行。

第一行包含三个正整数n,k 和m,分别表示知识点的数量,Kiana 希望少年自己想出来的联系的数量和联系的总数量。

接下来m 行,每行包含四个正整数a,b,Ti 和ti,表示在知识点a 和b 之间可以构造出一个联系,这个联系由少年自己想出来需要花费 Ti 的时间,而Kiana 直接讲出来需要花费ti 的时间。

Output

输出文件包括一行。

第一行包含一个正整数,表示构造的联系中耗时最长的一个的最短用时。

时间最长用时最短?

显然二分.

又因为最后要求连通即可,因此最后一定是一个最小生成树,

所以二分时间,跑\(Kruskal\)建树即可.

代码

#include<cstdio>
#include<cctype>
#define N 500008
#define R register
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int n,k,m,l,r=1e7,f[N],ans;
struct cod{int u,v,w;}edge[N],e[N];
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
inline bool ok(int x)
{
    R int cnt=0;
    for(R int i=1;i<=n;i++)f[i]=i;
    for(R int i=1;i<=m;i++)
    {
        R int u=edge[i].u,v=edge[i].v,w=edge[i].w;
        if(w>x)continue;
        R int fu=find(u),fv=find(v);
        if(fu!=fv)f[fu]=fv;
        cnt++;
    }
    if(cnt<k)return false;
    for(R int i=1;i<=m;i++)
    {
        R int u=e[i].u,v=e[i].v,w=e[i].w;
        if(w>x)continue;
        R int fu=find(u),fv=find(v);
        if(fu==fv)continue;
        f[fu]=fv;
    }
    R int ccc=0;
    for(R int i=1;i<=n;i++)
    {
        if(f[i]==i)
        {
            ccc++;
            if(ccc>1)return false;
        }
    }
    return true;
}
int main()
{
    in(n),in(k),in(m);
    for(R int i=1,a,b,T,t;i<=m;i++)
    {
        in(a),in(b),in(t),in(T);
        edge[i].u=a;edge[i].v=b;edge[i].w=t;
        e[i].u=a;e[i].v=b;e[i].w=T;
    }
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(ok(mid))r=mid-1,ans=mid;
        else l=mid+1;
    }
    printf("%d",ans);
}

转载于:https://www.cnblogs.com/-guz/p/9822667.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值