题解:[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;
}
posted @ 2026-07-23 21:52  TH911  阅读(1)  评论(0)    收藏  举报