【CF573E】Bear and Bowling

题面

洛谷

题解

首先有一个贪心的结论:

我们一次加入每个数,对于\(\forall i\),位置\(i\)的贡献为\(V_i = k_i\times a_i+b_i\),其中\(k_i\)为位置\(i\)之前被选的数的个数,\(b_i\)为\(i\)之后被选的数的和。

那么我们每次选这个贡献最大的位置一定最优。

然后来证一下这个结论的正确性(直接蒯了\(\text {I} \color{#FF0000} {\text {tst}}\)的):

引理:在上述贪心策略下,如果\(a_i>a_j\)且\(i<j\),则选\(i\)之前不可能选\(j\)。

证明考虑归纳:\(i,j\)中间不存在被选中的元素时是平凡的,如果\(i,j\)中间存在p个选中的元素,若\(V_i<V_j\)则一定在\([i,j]\)之间存在至少一个\(x\)满足\(a_x<a_j\),此时没有选\(i\)所以不可能选择\(x\),与假设不符,QED。

接下来假设贪心策略不正确,即在选择了集合\(A\)之后将下标为\(x\)的位置选中,但是最优的答案是选择集合\(A+B\),其中\(x\notin B\)。那么考虑:

1、如果\(B\)中存在位置在\(x\)左边,考虑在\(x\)左边的最右位置\(y\),那么此时有\(a_y\leq a_x,V_x≥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\)也不会更劣。

故上述假设不成立,贪心正确性证毕。

考虑分块维护这个最大值。

每次选完最大值,就相当于对于选定的\(pos\),\(pos\)前的位置\(i\)中\(b_i\)均加上\(a_{pos}\),\(pos\)后的位置\(i\)中\(k_i\)均加上一。因为对于同一块我们加上的\(k,b\)是一样的,而\(pos\)所在的块可以\(\sqrt n\)暴力重构,所以我们分块维护凸壳就可以了。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std; const int MAX_N = 1e5 + 5;
const int LEN = 320;
int N, a[MAX_N], id[MAX_N], bel[MAX_N], L[LEN], R[LEN];
long long tk[LEN], tb[LEN], f[MAX_N], ans;
double slope(int i, int j) {
if (a[i] == a[j]) return f[i] > f[j] ? -1e20 : 1e20;
else return (double)(f[i] - f[j]) / (a[i] - a[j]);
}
long long calc(int i) { return f[i] + tk[bel[i]] * a[i] + tb[bel[i]]; }
struct Hull {
deque<int> q;
void clear() { q.clear(); }
void insert(int i) {
while (q.size() > 1 && slope(q[q.size() - 2], q[q.size() - 1])
< slope(q[q.size() - 1], i)) q.pop_back();
q.push_back(i);
}
pair<long long, int> query() {
while (q.size() > 1 && calc(q[0]) <= calc(q[1]))
q.pop_front();
return make_pair(calc(q[0]), q[0]);
}
} S[LEN];
void modify(int l, int ed, int k, int b) {
while (l <= ed) {
int x = bel[l], r = min(R[x], ed);
if (l == L[x] && r == R[x]) tk[x] += k, tb[x] += b;
else for (int i = l; i <= r; i++) f[i] += 1ll * k * a[i] + b;
l = r + 1;
}
}
pair<long long, int> query(int l, int ed) {
pair<long long, int> res = make_pair(0, 0);
while (l <= ed) {
int x = bel[l], r = min(R[x], ed);
if (l == L[x] && r == R[x]) res = max(res, S[x].query());
else for (int i = l; i <= r; i++) res = max(res, make_pair(calc(i), i));
l = r + 1;
}
return res;
}
void build(int x) {
for (int i = L[x]; i <= R[x]; i++) f[i] += tk[x] * a[i] + tb[x];
tk[x] = tb[x] = 0, S[x].clear();
for (int i = L[x]; i <= R[x]; i++) S[x].insert(id[i]);
}
int main () {
#ifndef ONLINE_JUDGE
freopen("cpp.in", "r", stdin);
#endif
scanf("%d", &N);
for (int i = 1; i <= N; i++) {
scanf("%d", a + i);
f[i] = a[i], id[i] = i;
bel[i] = (i - 1) / LEN + 1;
if (!L[bel[i]]) L[bel[i]] = i;
R[bel[i]] = i;
}
for (int i = 1; i <= bel[N]; i++)
sort(&id[L[i]], &id[R[i] + 1], [](int l, int r) { return a[l] < a[r]; }), build(i);
long long ans = 0;
while (1) {
pair<long long, int> res = query(1, N);
if (res.first <= 0) break;
ans += res.first;
f[res.second] = -1ll << 60;
if (res.second > 1) modify(1, res.second - 1, 0, a[res.second]);
if (res.second < N) modify(res.second + 1, N, 1, 0);
build(bel[res.second]);
}
printf("%lld\n", ans);
return 0;
}

【CF573E】Bear and Bowling的更多相关文章

  1. 【CF573D】Bear and Cavalry 线段树

    [CF573D]Bear and Cavalry 题意:有n个人和n匹马,第i个人对应第i匹马.第i个人能力值ai,第i匹马能力值bi,第i个人骑第j匹马的总能力值为ai*bj,整个军队的总能力值为$ ...

  2. 【CF679D】Bear and Chase 最短路+乱搞

    [CF679D]Bear and Chase 题意:近日,鼠国的头号通缉犯,神出鬼没的怪盗——Joker正于摩登市出没!对于名侦探Jack来说,这正是将其捉拿归案的大号时机.形式化地,摩登市可以看成一 ...

  3. 【codeforces】Bear and Three Balls(排序,去重)

    Bear and Three Balls Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I6 ...

  4. 【CF771A】Bear and Friendship Condition

    题目大意:给定一张无向图,要求如果 A 与 B 之间有边,B 与 C 之间有边,那么 A 与 C 之间也需要有边.问这张图是否满足要求. 题解:根据以上性质,即:A 与 B 有关系,B 与 C 有关系 ...

  5. 【CodeForces】790 C. Bear and Company 动态规划

    [题目]C. Bear and Company [题意]给定大写字母字符串,交换相邻字符代价为1,求最小代价使得字符串不含"VK"子串.n<=75. [算法]动态规划 [题解 ...

  6. 【CodeForces】679 A. Bear and Prime 100

    [题目]A. Bear and Prime 100 [题意]有一数字x,每次可询问一个数字y是否x的因子,最后输出数字x是否素数,要求询问次数<=20. [题解]容易发现[2,100]范围内的非 ...

  7. 【CodeForces】679 B. Bear and Tower of Cubes

    [题目]B. Bear and Tower of Cubes [题意]有若干积木体积为1^3,2^3,...k^3,对于一个总体积X要求每次贪心地取<=X的最大积木拼上去(每个只能取一次)最后总 ...

  8. 【32.89%】【codeforces 574D】Bear and Blocks

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  9. 【19.05%】【codeforces 680D】Bear and Tower of Cubes

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

随机推荐

  1. TF-IDF & CNN

    TF-IDF----------------------------------------------------------------认为一个单词出现的文本频率越小,它区别不同类别的能力就越大, ...

  2. Delphi 10.3.2 社区版的安装

    1.去 https://www.embarcadero.com/cn/products/delphi/starter 下载安装程序 首先你要有一个embarcadero社区账号,国内线路下载有点慢,下 ...

  3. java的三种随机数生成方式

    随机数的产生在一些代码中很常用,也是我们必须要掌握的.而java中产生随机数的方法主要有三种: 第一种:new Random() 第二种:Math.random() 第三种:currentTimeMi ...

  4. 第13章 C#中的多线程

    章多线程 13.1 线程概述 计算机的操作系统多采用多任务和分时设计.多任务是指在一个操作系统中开以同时运行多个程序.例如,可以在使用QQ聊天的同时听音乐,即有多个独立的任务,每个任务对应一个进程,每 ...

  5. alpine使用的避坑指南

    alpine,是一个重量仅为5 MB的最小Linux发行版.它还有基本的linux工具和一个不错的包管理器APK.APK非常稳定,有相当数量的包.由于体积小,在容器中很受欢迎,但是使用上坑也很多,大部 ...

  6. python 正则表达式、re

    正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配. 参考链接:https://www.runoob.com/python/python-reg-expressions. ...

  7. Golang中设置函数默认参数的优雅实现

    在Golang中,我们经常碰到要设置一个函数的默认值,或者说我定义了参数值,但是又不想传递值,这个在python或php一类的语言中很好实现,但Golang中好像这种方法又不行.今天在看Grpc源码时 ...

  8. Java NIO学习系列一:Buffer

    前面三篇文章中分别总结了标准Java IO系统中的File.RandomAccessFile.I/O流系统,对于I/O系统从其继承体系入手,力求对类数量繁多的的I/O系统有一个清晰的认识,然后结合一些 ...

  9. 基于vue+springboot+docker网站搭建【五】部署vue前端项目

    部署vue前端项目  一.下载项目到本地   https://github.com/macrozheng/mall-admin-web 二.npm install 三.修改api配置,改为你接下来要部 ...

  10. 2019-09-17 thinkphp网页静态化

    public function details(){ $pid = I('get.goods_id'); $filename = "details_".$pid.".ht ...