传送门


题解搬运工++

先证明一个贪心做法的正确性:做以下操作若干次,每一次考虑选择没有被选到答案序列中的数加入到答案序列中对答案的贡献,设第\(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;
}

最新文章

  1. 互联网+下PDA移动智能手持POS超市收银开单软件
  2. SeleniumIDE从0到1 (Selenium IDE 回放)
  3. Oracle数据库体系结构、启动过程、关闭过程
  4. (转)linux下cp目录时排除一个或者多个目录的实现方法
  5. Objective-C( Category 分类,非正式协议,分类延展)
  6. mysql库大小
  7. MySQL注入load_file常用路径
  8. 【风马一族_Android】适合你 --- 大概的描述
  9. mongodb中分页显示数据集的学习
  10. Android基础笔记(十)- 帧动画、补间动画具体解释、对话框
  11. [拾 得] 一枚迷人的贝壳 SHELL / Linux | shell 脚本初步入门
  12. SourceTree 03 - 跳过账号登录直接进入主界面
  13. linux 计划任务 访问某个URL
  14. scrapy安装问题记录
  15. 20175202 《Java程序设计》迭代和JDB
  16. flask 模版语言及信息传递
  17. go test :Wrong test signature
  18. Objective-C 图片处理
  19. Matlab 沿三维任意方向切割CT图的仿真计算
  20. UVALive 4254 Processor(二分)

热门文章

  1. Test CMake run finished with errors
  2. Python字符编码和转码
  3. Linux shell 中断循环语句
  4. ubuntu无法连接网络
  5. Graylog-Sidecar
  6. flask获取get请求传过来的数组
  7. Game Publisher
  8. redis主从+redis的哨兵模式
  9. [PHP] Laravel使用redis保存SESSION
  10. 洛谷P1894 [USACO4.2]完美的牛栏The Perfect Stall题解