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了之后才发现时间复杂度是错的. 但是贪心思想一定是对的,每 ...
随机推荐
- 源码安装rlwrap 0.43(为了方便使用linux下的sqlplus)
为了linux下的sqlplus方便调用历史命令和退格,安装下rlwrap,最新版本是0.43,貌似作者已经不更新了 下载地址 https://fossies.org/linux/privat/rlw ...
- TL-WDN5200H无线usb网卡在Linux上的使用
买了个TL-WDN5200H无线usb网卡,但是发现它居然不支持Linux,但是我有时需要在Linux上使用,这就尴尬了.于是到网上搜索资料,终于解决了这个问题. 首先编译安装:https://git ...
- 《浅谈我眼中的express、koa和koa2》好文留存+笔记
原文 :三英战豪强,思绪走四方.浅谈我眼中的express.koa和koa2 一.回调大坑怎么解决呢? 1.es5可以利用一下第三方库,例如 async 库, 2.或者单纯使用 connect中间件 ...
- Python基础B(数据类型----交互)
数据类型 数字类型 一.整型(int) age = 18 % age=int(18) print(id(age)) print(type(age)) print(age) 4530100848 < ...
- NOIP 2003 栈
洛谷 P1044 栈 洛谷传送门 JDOJ 1291: [NOIP2003]栈 T3 JDOJ传送门 题目描述 栈是计算机中经典的数据结构,简单的说,栈就是限制在一端进行插入删除操作的线性表. 栈有两 ...
- Centos安装JDK(java环境)
王小私下问我 centos 中 jdk 怎么安装呀,所以再次整理了这篇基础环境搭建的文章. 1.创建java目录2.下载上传jdk3.解压jdk4.配置环境变量 1.创建java目录 首先我们创建ja ...
- 原生PHP+原生ajax批量删除(超简单),ajax删除,ajax即点即改,完整代码,完整实例
效果图: 建表:company DROP TABLE IF EXISTS `company`;CREATE TABLE `company` ( `id` int(11) NOT NULL AUTO_I ...
- CPU中断的工作原理,从最底层讲起
前言 中断的概念属于硬件层.虽然我们在进行软件编程时不会直接使用中断,但理解它对我们来说依然重要. 我们在使用线程切换及状态管理.异常处理.硬件与处理器的交互.I/O操作等指令时,中断都在默默的为我们 ...
- python源码解剖
print()本身就是用了多态:不同类型的对象,其实是调用了自身的print()方法 多态:动物 狗1 = new狗() 用公共的部分来指定类型,实则是调用各自的属性 创建对象有两种方式: 通过C A ...
- selenium--高亮显示正在操作的元素
前戏 在进行web自动化的时候,如果我们想知道正在操作的元素,我们可以通过js的方式来实现 实战 from selenium import webdriver import unittest, tim ...