[LGOJ]P4951[USACO 2001 OPEN]地震[二分答案,kruskal]

ans=(f-sum(c))/sum(ti)
f-sum(c)-ans*sum(ti)=0 f关于ans单调
即 f=sum(c+ans*ti), 二分查找ans即可
chk()中对于每个ans把c_i+ans*ti_i作为e[i]的边权跑最小生成树即可判断与f关系
//本程序中f叫做ct(cost_total)

#include<cstdio>
#include<algorithm>
#define int long long
#define db double
#define in inline
#define re register
in int read()
{
    int s=0,b=0;
    char ch;
    do{
        ch=getchar();
        if(ch=='-') b=1;
    }while(ch<'0' || ch>'9');
    while(ch>='0' && ch<='9')
    {
        s=(s<<3)+(s<<1)+(ch^48);
        ch=getchar();
    }
    return b?-s:s;
}
in db fabs(db x)
{
    return x>0?x:-x;
}
int n,m,ct;
struct edge{
    int f,t,c,ti;
    db w;
    bool operator < (const edge &t)const
        {
            return w<t.w;
        }
}e[10010];
int f[500];
int findd(int x)
{
    if(f[x]!=x) f[x]=findd(f[x]);
    return f[x];
}
bool chk(db ans)
{
    for(re int i=1;i<=n;++i) f[i]=i;
    for(re int i=1;i<=m;++i) e[i].w=(db)e[i].ti*ans+(db)e[i].c;
    std::sort(e+1,e+m+1);
    int cnt=0;
    db sum=0;
    for(re int i=1;i<=m;++i)
    {
        int r1=findd(e[i].f),r2=findd(e[i].t);
        if(r1==r2) continue;
        f[r1]=r2;
        sum+=e[i].w;
        ++cnt;
        if(cnt==n-1) break;
    }
    if(sum>ct) return false;
    else return true;
}
signed main()
{
    n=read();
    m=read();
    ct=read();
    for(re int i=1;i<=m;++i)
        e[i].f=read(),e[i].t=read(),e[i].c=read(),e[i].ti=read();
    db l=0,r=1e20,eps=1e-6;
    while(fabs(l-r)>eps)//实数域二分
    {
        db mid=(l+r)/2;
        if(chk(mid)) l=mid;
        else r=mid;
    }
    printf("%0.4lf\n",l);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/cgazn/p/10387957.html
今日推荐