「SCOI2014」方伯伯运椰子 解题报告

时间:2021-12-06 20:42:27

「SCOI2014」方伯伯运椰子

可以看出是分数规划

然后我们可以看出其实只需要改变1的流量就可以了,因为每次改变要保证流量守恒,必须流成一个环,在正负性确定的情况下,变几次是无所谓的。

然后按照套路,设
\[ ans=\frac{X-Y}{k}\\ ans\times k =X-Y\\ ans\times k=-\sum w_i\\ \sum ans-w_i=0 \]
从第二部到第三步是把X和Y中的共同边都减掉了

\(w\)是根据扩容或者缩容建的边权为\(b+d,a-d\)的边权集合

注意一点,缩小容量必须\(c_i>0\)

然后发现环的边数就是\(k\),减过去就可以二分ans了


Code:

#include <cstdio>
#include <cctype>
#include <cstring>
template <class T>
void read(T &x)
{
    x=0;char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) x=x*10+c-'0',c=getchar();
}
const int N=5010;
const int M=6010;
int n,m,u[N],v[N],a[N],b[N],c[N],d[N];
int head[N],to[M],Next[M],cnt;double edge[M];
void add(int u,int v,double w)
{
    to[++cnt]=v,edge[cnt]=w,Next[cnt]=head[u],head[u]=cnt;
}
int used[N],vis[N],q[N*N],l,r;double dis[N];
bool spfa()
{
    for(int i=1;i<=n+2;i++) dis[i]=1e12;
    memset(vis,0,sizeof vis);
    memset(used,0,sizeof used);
    dis[q[l=r=1]=n+1]=0;
    while(l<=r)
    {
        int now=q[l++];
        used[now]=0;
        for(int v,i=head[now];i;i=Next[i])
            if(dis[v=to[i]]>dis[now]+edge[i])
            {
                dis[v]=dis[now]+edge[i];
                if((++vis[v])==n+2) return true;
                if(!used[v]) used[q[++r]=v]=1;
            }
    }
    return false;
}
bool check(double x)
{
    memset(head,0,sizeof head),cnt=0;
    for(int i=1;i<=m;i++)
    {
        if(u[i]!=n+1)
        {
            if(c[i]!=0) add(v[i],u[i],x+a[i]-d[i]);
            add(u[i],v[i],x+b[i]+d[i]);
        }
        else
            add(u[i],v[i],0);
    }
    return spfa();
}
int main()
{
    read(n),read(m);
    double l=0,r=0;
    for(int i=1;i<=m;i++)
    {
        read(u[i]),read(v[i]),read(a[i]);
        read(b[i]),read(c[i]),read(d[i]);
        r+=1.0*c[i]*d[i];
    }
    while(l+1e-6<r)
    {
        double mid=(l+r)/2;
        if(check(mid)) l=mid;
        else r=mid;
    }
    printf("%.2lf\n",l);
    return 0;
}

2019.2.24