Atcoder Regular Contest 125 E - Snack(最小割转化+贪心)
Preface:
这是生平第一道现场 AC 的 arc E,也生平第一次经历了 performance \(\ge 2800\),甚至还生平第一次被 hb 拉到会议里讲题,讲的就是这个题,然鹅比较尬的一点是我不知道腾讯会议开了白板之后不能看到电脑,导致我的 typora 没人能看到……所以就暂且把我 typora 的内容整了整改成了一篇题解(
题意:
- 有 \(n\) 种零食,第 \(i\) 种零食有 \(a_i\) 个。
- 有 \(m\) 个人领这些零食,第 \(i\) 个人最多领 \(b_i\) 个同种零食
- 第 \(i\) 个人领零食的总数不能超过 \(c_i\)
- 求最多分出去多少零食
- \(n,m\le 2\times 10^5\)
首先考虑网络流建图,我们新建源 \(S\) 汇 \(T\),将零食看作左部点 \(X_i\),人看作右部点 \(Y_i\),那么我们可以建出以下几类边:
- \(S\to X_i\),容量 \(a_i\)
- \(X_i\to Y_j\),容量 \(b_j\)
- \(Y_i\to T\),容量 \(c_i\)
然后跑最大流就是答案。直接建图复杂度爆炸,考虑最大流&最小割定理转化为最小割问题。也就是割掉权值最小的边集使得 \(S,T\) 不连通。下记 \(S\to X_i\) 为第一类边,\(X_i\to Y_j,Y_i\to T\) 分别记作第二、三类边。
考虑枚举保留多少个一类边,记作 \(x\),那我们割掉的一类边肯定是最小的 \(n-x\) 条,排序+前缀和求解即可。对于一个 \(Y_j\),目前有用的边肯定只剩下 \(x\) 条与 \(Y_j\) 相连的二类边和 \(Y_j\) 连向汇点的三类边。删掉与 \(j\) 有关的边的代价是 \(\min(c_j,b_j·x)\)。将所有右部点按照 \(\dfrac{c_j}{b_j}\) 排序后 \(\min\) 取到 \(c_j\) 的部分一定是一段前缀,\(\min\) 取到 \(b_j·x\) 的部分肯定是对应部分的一个补集,也就是一段后缀。我们在枚举 \(x\) 的过程中 two pointers 找出这段前缀,然后预处理出 \(b,c\) 的前缀和即可 \(\mathcal O(1)\) 计算贡献。
时间复杂度 \(n\log n\)(如果 \(n,m\) 视作同阶),注意精度,直接比较 double 可能会获得 AC \(\times\) 26,WA \(\times\) 1 的好成绩。
const int MAXN=2e5;
int n,m,ord[MAXN+5];
ll a[MAXN+5],b[MAXN+5],c[MAXN+5];
ll suma[MAXN+5],sumb[MAXN+5],sumc[MAXN+5];
bool cmp(int x,int y){return 1ull*c[x]*b[y]<1ull*b[x]*c[y];}
int main(){
scanf("%d%d",&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]);
for(int i=1;i<=m;i++) ord[i]=i;
sort(a+1,a+n+1);sort(ord+1,ord+m+1,cmp);
for(int i=1;i<=n;i++) suma[i]=suma[i-1]+a[i];
for(int i=1;i<=m;i++) sumb[i]=sumb[i-1]+b[ord[i]];
for(int i=1;i<=m;i++) sumc[i]=sumc[i-1]+c[ord[i]];
ll res=1e18;
for(int i=0;i<=n;i++){
int l=1,r=m,p=0;
while(l<=r){
int mid=l+r>>1;
if(1ll*b[ord[mid]]*i<=c[ord[mid]]) r=mid-1;
else p=mid,l=mid+1;
} //printf("%d %d\n",i,p);
chkmin(res,suma[n-i]+sumc[p]+1ll*(sumb[m]-sumb[p])*i);
} printf("%lld\n",res);
return 0;
}
原文地址:https://www.cnblogs.com/ET2006/p/arc125E.html
- SpringSecurity中密码加盐与SpringBoot中异常统一处理
- SpringBoot+Vue前后端分离,使用SpringSecurity完美处理权限问题(二)
- SpringBoot+Vue前后端分离,使用SpringSecurity完美处理权限问题(一)
- Java操作MongoDB
- 初识MongoDB分片
- Linux上安装Redis
- SpringBoot+WebSocket实现在线聊天(二)
- SpringBoot中使用Freemarker构建邮件模板
- SpringBoot中发送QQ邮件
- SpringBoot中使用POI,快速实现Excel导入导出
- 代理技术 | 重磅,代理服务器背后的故事(正向、反向代理)
- SpringBoot中自定义参数绑定
- ElementUI中tree控件踩坑记
- 一个隐马尔科夫模型的应用实例:中文分词
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 算法复杂度(二)
- 线性表--顺序表--单向链表(四)
- C语言俄罗斯方块(新版本完整代码)
- 线性表--顺序表--双向链表(六)
- C/C++什么时候使用二级指针,你知道吗?
- 萌新学习C++容易漏掉的知识点,看看你中招了没有(一)
- 萌新不看会后悔的C++string字符串常用知识点总结
- salesforce零基础学习(九十六)项目中的零碎知识点小总结(四)
- CodeForces - 260C
- 疯子的算法总结(九) 图论中的矩阵应用 Part 2 矩阵树 基尔霍夫矩阵定理 生成树计数 Matrix-Tree
- STL常用对象,不会搞得C++跟没学一样
- 桥接模式
- CF--思维练习--CodeForces - 220C Little Elephant and Shifts (STL模拟)
- CF--思维练习--CodeForces - 221C-H - Little Elephant and Problem (思维)
- CF--思维练习--CodeForces - 219C Color Stripe (思维)