CF573E Bear and Bowling 贪心、分块、凸包
题解搬运工++
先证明一个贪心做法的正确性:做以下操作若干次,每一次考虑选择没有被选到答案序列中的数加入到答案序列中对答案的贡献,设第\(i\)个位置的贡献为\(V_i\),如果最大的贡献小于0则退出,否则选择其中贡献最大的加入答案序列中。
首先一个引理:在上述贪心策略下,如果\(a_i\)>\(a_j\)且\(i\)<\(j\),则选\(i\)之前不可能选\(j\)。
证明考虑对 \(i,j\) 中间选中的元素的数量归纳:\(i,j\) 中间不存在被选中的元素时是平凡的,如果 \(i,j\) 中间存在 \(p\) 个选中的元素且 \(V_i\)<\(V_j\),则一定在 \([i,j]\) 间存在至少一个被选中的元素 \(x\) 满足 \(a_x\)<\(a_i\),根据归纳在选 \(i\) 之前不可能选 \(x\),矛盾,故不可能存在这样的 \(x\),所以 \(V_i\) > \(V_j\),QED
。
接下来假设贪心策略不正确,即在选择了集合\(A\)之后将下标为\(x\)的位置选中,但是最优的答案是选择集合\(A+B\),其中\(x \not\in B\)。那么考虑:
1、如果\(B\)中存在位置在x左边,考虑在\(x\)左边的最右位置\(y\),那么此时有\(a_y \leq a_x , V_x \geq V_y\)。此时加入集合\(B\)中的其他元素考虑\(V_x,V_y\)的变化,那么在\(x\)右边的元素对\(V_x,V_y\)的贡献一样,在\(y\)左边的元素对\(V_x,V_y\)的贡献是\(a_x,a_y\),而\(x,y\)中间没有在\(B\)中的元素,所以可以发现在其他元素加入之后\(V_x \geq V_y\),所以将\(B\)中\(y\)换成\(x\)结果不劣。
2、如果\(B\)中只有在\(x\)右边的元素,考虑在\(x\)右边的最左位置\(y\),那么\(B\)集合其他的元素对\(V_x,V_y\)的贡献是一样的,所以把\(y\)换成\(x\)也不会更劣。
故上述假设不成立,贪心正确性证毕。
那么我们考虑选择了一个元素之后对答案的影响。假如选择了位置\(x\),那么当\(y < x\)时,\(V_y += x\);当\(y > x\)时,\(V_y += a_y\)。那么每一个时刻\(V_y\)都可以表示成\(ka_y+b\)的形式,用分块维护凸包即可。
#include<cstdio>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<bitset>
#include<random>
#include<unistd.h>
//This code is written by Itst
using namespace std;
#define int long long
#define PII pair < int , int >
#define ld long double
const int _ = 1e5 + 7 , T = sqrt(_) + 5;
int arr[_] , add[_] , mrk[T] , id[_] , N;
ld sect(PII p , PII q){return 1.0 * (q.second - p.second) / (p.first - q.first);}
int calc(PII p , int q){return p.first * q + p.second;}
struct Hull{
deque < PII > q; int mrk;
int get(){while(q.size() >= 2 && calc(q[0] , mrk) <= calc(q[1] , mrk)) q.pop_front(); return calc(q[0] , mrk);}
void up(){++mrk;}
void rebuild(int bl){
q.clear(); mrk = 0;
for(int i = bl * T ; i < N && i < (bl + 1) * T ; ++i){
PII now(arr[id[i]] , add[id[i]]);
if(q.size() && q.back().first == now.first)
if(q.back().second > now.second) continue;
else q.pop_back();
while(q.size() >= 2 && sect(q[q.size() - 2] , now) < sect(q[q.size() - 2] , q[q.size() - 1])) q.pop_back();
q.push_back(now);
}
}
}now[T];
void down(int bl){
for(int i = T * bl ; i < N && i < (bl + 1) * T ; ++i)
add[i] += now[bl].mrk * arr[i] + mrk[bl];
mrk[bl] = 0;
}
signed main(){
scanf("%lld" , &N);
for(int i = 0 ; i < N ; ++i){scanf("%lld" , &arr[id[i] = i]); add[i] = arr[i];}
for(int i = 0 ; i < N ; i += T){
sort(id + i , id + min(T + i , N) , [&](int p , int q){return arr[p] < arr[q];});
now[i / T].rebuild(i / T);
}
int sum = 0;
while(1){
int mx = -1e18 , id = -1;
for(int i = 0 ; i < N / T + (bool)(N % T) ; ++i) if(mx < now[i].get() + mrk[i]){mx = now[i].get() + mrk[i]; id = i;}
if(mx < 0) break;
sum += mx; down(id);
for(int i = T * id ; i < (id + 1) * T ; ++i)
if(add[i] == mx){
for(int j = 0 ; j < id ; ++j) mrk[j] += arr[i];
for(int j = id + 1 ; j < N / T + (bool)(N % T) ; ++j) now[j].up();
for(int j = T * id ; j < i ; ++j) add[j] += arr[i];
add[i] = -1e15;
for(int j = i + 1 ; j < N && j < (id + 1) * T ; ++j) add[j] += arr[j];
now[id].rebuild(id);
break;
}
}
printf("%lld\n" , sum); return 0;
}
CF573E Bear and Bowling 贪心、分块、凸包的更多相关文章
- CF573E Bear and Bowling(6-1)
题意 洛谷 做法一 考虑一种贪心(先别管对不对),设当前已选择的集合为\(A\),这是考虑该集合的补集,每个元素加进来后的增量为\(V_i\),则挑选最大的那个加入该集合 结论1:遵循上述贪心,\(\ ...
- CF573E Bear and Bowling
题目 我们设\(f_{i,j}\)表示前\(i\)个数中选\(j\)个的最大值. 那么显然有\(f_{i,j}=max(f_{i-1,j},f_{i-1,j-1}+j*a_i)\). 这个东西我们首先 ...
- 【CF573E】Bear and Bowling
[CF573E]Bear and Bowling 题面 洛谷 题解 首先有一个贪心的结论: 我们一次加入每个数,对于\(\forall i\),位置\(i\)的贡献为\(V_i = k_i\times ...
- Codeforces 660F Bear and Bowling 4 斜率优化 (看题解)
Bear and Bowling 4 这也能斜率优化... max[ i ] = a[ i ] - a[ j ] - j * (sum[ i ] - sum[ j ])然后就能斜率优化啦, 我咋没想到 ...
- CodeForces - 660F:Bear and Bowling 4(DP+斜率优化)
Limak is an old brown bear. He often goes bowling with his friends. Today he feels really good and t ...
- BZOJ 2388: 旅行规划 [分块 凸包 等差数列]
传送门 题意: 区间加和询问一段区间内整体前缀和的最大值 刚才还在想做完这道题做一道区间加等差数列结果发现这道就是.... 唯一的不同在于前缀和一段区间加上等差数列后,区间后面也要加上一个常数!!! ...
- 2019.01.20 bzoj2388: 旅行规划(分块+凸包)
传送门 分块好题. 题意:维护区间加,维护区间前缀和的最大值(前缀和指从1开始的). 思路: 考虑分块维护答案. 我们把每个点看成(i,sumi)(i,sum_i)(i,sumi)答案一定会在凸包上 ...
- bzoj2388(分块 凸包)
好像没有什么高级数据结构能够很高效地实现这个东西: 那就上万能的分块,我们用一些数形结合的思想,把下标看成横坐标,前缀和的值看成纵坐标: 给区间内每个数都加k相当于相邻两点的斜率都加上k: 这种东西我 ...
- 2018.09.17 atcoder Tak and Hotels(贪心+分块)
传送门 一道有意思的题. 一开始想错了,以为一直lowerlowerlower_boundboundbound就可以解决询问,结果交上去TLE了之后才发现时间复杂度是错的. 但是贪心思想一定是对的,每 ...
随机推荐
- 被严重误会?APS系统没有想象的那么复杂
APS的出现要从90年代了,但到现在,很多行业内的顾问或用户提到APS都马上想到的是“要求很精确”“难度很大”“脱离实际”“太理想化”“工作量太大”等等,然后把它束之高阁不睬. 在这里,给大家分析一下 ...
- android studio学习----添加项目依赖包总结
Gradle Library Projects Gradle 项目可以依赖于其它组件.这些组件可以是外部二进制包,或者是其它的 Gradle 项目. 在本例中, app/build.gradle 中有 ...
- Java进程间通信学习
转自:https://www.iteye.com/blog/polim-1278435 进程间通信的主要方法有:(1)管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共 ...
- 面向对象(三)--多态、封装、property装饰器
一.多态与多态性 1.什么是多态 多态指的是同一种类/事物的不同形态 class Animal: def speak(self): pass class People(Animal): def spe ...
- Jmeter 使用自定义变量
有些情况下比如发起测试时URL的主机名和端口需要在采样器中出现多次,这样就有个问题,当测试的主机更改时, 我们需要修改主机名称,这时就需要修改多个地方,如果多的情况会有遗漏.如果我们在配置脚本的时候, ...
- Python从零开始——安装与运行
- <pre> 保留文本格式显示在网页上
<code> 标签 解释:保留输入的格式空格等不变,原样显示在网页上 例如: <pre> 通知 即日起不再提供公共设施 个店铺需自行准备. 望周知~!! 2020/10/10 ...
- axios 下载文件
axio请求里必须加 responseType: 'blob' 参数,如下 //下载文件 api.download=function(id) { return request({ url: this ...
- Linux传输指令-scp
本地传到远程 scp -rf bt.plist cjp@centos:~/下载 从远程下载到本地 scp -rf cjp@centos:~/下载 ~ 参数详解 参数 描述 -a 尽可能将档案状态.权限 ...
- Win10下默认显示文件的扩展名
默认不显示文件的后缀名 1.右击此电脑,点击属性 2.点击控制面板主页 3.点击文件资源管理器选项 4.切换到查看选项卡,取消勾选“隐藏已知文件类型的扩展名”,应用->确定 结果: