%%% 神仙 tzc 爆切 arcE!
一眼 wll:源点连向所有零食 (i),容量为 (a_i);所有零食 (i) 连向所有小孩 (j),容量为 (b_j);所有小孩 (j) 连向汇点,容量为 (c_j)。跑最大流。但是这个图的边数是 1e10,存都存不下,就不会做了。
然后就是非常神仙的思路了 %%%%%:将最大流转化成最小割(u1s1 用最大流做最小割很正常,但是用最小割做最大流还真的没见过)。我们知道最小割要比最大流显式得多,所以在特殊构造的图里面最小割往往更好求。
先来扯这么一件事:很多人以为割的定义是一个边集使得割掉之后 (s o t) 不连通,其实不然,这玩意叫做「割边集」;割的定义其实是对点集的一个划分 (S,T) 使 (sin S,tin T),(S o T) 所有边的集合为一个割。但是很好的事情是:在边权都非负的图上,最小割等于最小割边集,这样我们就可以求更直观的最小割边集 instead of 定义比较棘手的最小割了。证明:显然所有割都是割边集,所以最小割 ≥ 最小割边集;对每个割边集,从 (s) 开始 dfs 显然到不了 (t),令所有到的节点集合为 (S),考虑割 ((S,V-S)),这显然是原割边集去掉一些边,由于边权是正的,所以边权和不升,得到最小割 ≤ 最小割边集。得证。
有了这个思路就不算难了。设 (a) 边割掉的序列为 (x),(c) 边割掉的序列为 (y),此时最小割边集显然为 (sumlimits_{i=1}^{|x|}{a_{x_i}}+sumlimits_{i=1}^{m}egin{cases}c_i&iin y\(n-|x|)b_i&i otin yend{cases})。注意到右边的 sum 跟 (x) 具体是多少无关,只跟 (|x|) 有关,所以在 (|x|) 确定的情况下应该选尽可能小的 (a_{x_i}),那就排个序前缀和。考虑枚举 (|x|),右边那项最优决策显然是 (sumlimits_{i=1}^mmin(c_i,(n-|x|)b_i))。考虑随 (|x|) 变化维护这玩意,显然对每个 (i) 决策是单调的,即存在分界点使得一边全选 (c_i) 一边全选 ((n-|x|)b_i)。那就预处理出所有分界点(除法上取整即可)存到对应位置 todo-list 里,随便搞就行了。不算排序,复杂度线性。
code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pb push_back
const int inf=0x3f3f3f3f3f3f3f3f;
const int N=200010;
int n,m;
int a[N],b[N],c[N];
vector<int> chg[N];
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++)scanf("%lld",a+i);
for(int i=1;i<=m;i++)scanf("%lld",b+i);
for(int i=1;i<=m;i++)scanf("%lld",c+i);
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)a[i]+=a[i-1];
int ans=inf,sum=0,now=0;
for(int i=1;i<=m;i++){
now+=b[i];
int x=(c[i]+b[i]-1)/b[i];
if(x>n)continue;
chg[n-x].pb(i);
}
for(int i=n;~i;i--){
ans=min(ans,a[i]+sum);
if(i){
for(int j=0;j<chg[i-1].size();j++){
int x=chg[i-1][j];
now-=b[x];
sum+=-(n-i)*b[x]+c[x];
}
sum+=now;
}
}
cout<<ans;
return 0;
}