ARC125E Snack

小清新网络流优化题

首先不难想到一个 trivial 的网络流模型,即建立源点 \(S\) 和汇点 \(T\)

对于每个食物 \(i\),连 \(S\to i\),容量为 \(A_i\) 的边;对于每个人 \(j\),连 \(j\to T\),容量为 \(C_j\) 的边;同时所有食物向每个人 \(j\) 连容量为 \(B_j\) 的边

直接跑 Dinic 复杂度显然爆了,但这个图具有很强的性质,考虑手动找出它的最大流

首先用最大流最小割定理转为求最小割,枚举最后要断掉和 \(S\) 相邻的 \(x\) 条边

手玩发现由于左边每个点向右连出的边都相同,因此显然是断开最小的 \(x\) 条边

再考虑此时右边的每个点 \(j\),要么断开它和 \(T\) 的边,代价为 \(C_j\);要么断开中间的边,代价为 \((n-x)\times B_j\)

考虑从小到大枚举 \(x\),把人按照 \(\frac{C_j}{B_j}\) 排序后 two pointers 扫一遍即可

#include<cstdio>
#include<iostream>
#include<algorithm>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
typedef __int128 i128;
const int N=200005;
struct ifo
{
    int b,c;
    friend inline bool operator < (const ifo& A,const ifo& B)
    {
        return i128(A.c)*B.b>i128(A.b)*B.c;
    }
}p[N]; int n,m,a[N];
signed main()
{
    scanf("%lld%lld",&n,&m);
    for (RI i=1;i<=n;++i) scanf("%lld",&a[i]);
    for (RI i=1;i<=m;++i) scanf("%lld",&p[i].b);
    for (RI i=1;i<=m;++i) scanf("%lld",&p[i].c);
    sort(a+1,a+n+1); sort(p+1,p+m+1);
    int ans=1e18,suma=0,sumb=0,sumc=0;
    for (RI i=1;i<=m;++i) sumc+=p[i].c;
    for (RI i=0,j=1;i<=n;++i)
    {
        while (j<=m&&(n-i)*p[j].b<=p[j].c)
        sumb+=p[j].b,sumc-=p[j].c,++j;
        suma+=a[i]; ans=min(ans,suma+(n-i)*sumb+sumc);
    }
    return printf("%lld",ans),0;
}
posted @ 2024-08-14 19:28  空気力学の詩  阅读(42)  评论(0编辑  收藏  举报