题解:[ARC125E] Snack
题意分析
考虑网络流建模。
设 \(X_i,Y_j\) 表示第 \(i\) 颗糖果和第 \(j\) 个小朋友。
设源汇点 \(s,t\),连容量为 \(a_i\) 的边 \(s\rightarrow i\),容量为 \(b_j\) 的边 \(X_i\rightarrow Y_j\),容量为 \(c_j\) 的边 \(Y_j\rightarrow t\)。答案即最大流。
但是这样的边数为 \(\mathcal O(nm)\),直接跑最大流的复杂度不可接受,发现中间有很多边是相似的,全部建出来再跑最大流是不优的。
因为最大流最小割定理,除开源汇点这又是一个二分图,比较特别,考虑求最小割。
如果割掉了 \(x\) 个 \(s\rightarrow X_i\),那么小朋友 \(j\) 在 \(s\) 集合内需要割掉 \(xb_j\),在 \(t\) 集合内要割掉 \(c_j\)。
将 \(a_i\) 从小到大排序,则答案为:
\[\min_{x=0}^n\left(\sum_{i=0}^{n-x}a_i+\sum_{i=1}^n\min(xb_i,c_i)\right)
\]
直接维护是 \(\mathcal O\left(n^2\right)\) 的,不能通过 \(n\leq2\times10^5\) 的数据。
考虑 \(a_i\) 可以前缀和;对于后面的 \(\min(xb_i,c_i)\),可以按照 \(\dfrac{c_i}{b_i}\) 从小到大排序,之后一段的贡献是 \(xb_i\),一段的贡献是 \(c_i\)。维护一个指针再分别维护 \(c,b\) 的前后缀和即可。
时间复杂度 \(\mathcal O(n\log n)\)。
AC 代码
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
typedef long long ll;
typedef __int128 lll;
constexpr const int N=2e5,M=2e5;
constexpr const ll inf=0x3f3f3f3f3f3f3f3f;
int n,m;
ll a[N+1],pre[M+1],suf[M+1+1];
struct node{
ll b,c;
}q[M+1];
int main(){
/*freopen("test.in","r",stdin);
freopen("test.out","w",stdout);*/
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=m;i++){
cin>>q[i].b;
}
for(int i=1;i<=m;i++){
cin>>q[i].c;
}
sort(a+1,a+n+1);
sort(q+1,q+m+1,[](node a,node b){
return (lll)a.c*b.b<(lll)b.c*a.b;
});
for(int i=1;i<=n;i++){
a[i]+=a[i-1];
};
for(int i=m;1<=i;i--){
suf[i]=suf[i+1]+q[i].b;
}
for(int i=1;i<=m;i++){
pre[i]=pre[i-1]+q[i].c;
}
ll ans=inf;
for(int x=0,p=0;x<=n;x++){
while(p+1<=m&&x*q[p+1].b>=q[p+1].c){
p++;
}
ans=min(ans,a[n-x]+pre[p]+x*suf[p+1]);
}
cout<<ans<<'\n';
cout.flush();
/*fclose(stdin);114514
fclose(stdout);*/
return 0;
}

浙公网安备 33010602011771号