【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. tomcat闪退的解决思路

    用Tomcat总会遇到启动Tomcat闪退的问题. 什么叫闪退啊,就是闪一下,就退出了控制台. 都闪退了,为啥闪退也不知道呀,又没有错误信息,所以就要先阻止闪退,先看到错误信息,知道启动不起来的原因. ...

  2. golang学习笔记---string && strconv

    1.字符串的组成?Golang的字符串都是由单个字节连接起来的,每个字节都是UTF8编码标识的Unicode文本.(不需要在考虑中文不兼容问题) 2.如何遍历字符串?先看一个例子: package m ...

  3. python输入一个字符串,输出翻转后的字符串(翻转字符串)

    题目:输出一个字符串,输出翻转后的字符串.例如:输入字符串a123,输出321a. 方法一:使用列表的reverse方法 string=input('请输入一个字符串:') lst=list(stri ...

  4. WPF DispatcherTimer一些个人看法 (原发布 csdn 2017-04-25 19:12:22)

    wpf中的DispatcherTimer基本用法,本文不在叙述.主要写一些不同的,来提醒自己不要再犯同样错误. 前几天写代码时发现.当在非UI线程创建DispatcherTimer实例时,程序无法进入 ...

  5. Application类-欢迎页(初始界面)

    在程序界面显示前,如果我们处理了很多耗时操作,这个时候给用户提示一个欢迎页便是十分友好的.WPF为我们提供了这个特性: 第一种方法:通过设置图片资源的生成操作 粘贴一个图片到我们的项目中 在解决方案管 ...

  6. C#左移运算符

     << 左移操作符.简单理解是对变量进行与2的n次乘方的运算.比如x<<1=x*2x<<2=x*4x<<3=x*8x<<4=x*16依此类推 ...

  7. c# 对XML进行数字签名并且让java验签成功

    实现: 1.c#将xml报文做数字签名发送到java服务,java服务成功验签. 2.c#服务对收到java服务推送的xml报文成功验签. 前提: 1.java服务要求 遇到问题: 1.Java和.n ...

  8. chrome(谷歌)浏览器字体发虚解决办法

    chrome(谷歌浏览器)浏览网页时,字体发虚的解决办法: 1.点击chrome里的 “设置” - 外观 - 字体,改为 微软雅黑,该方法测试无效. 2.将系统字体的pingfang字体卸载,完美解决 ...

  9. Java线程安全面试题,你真的了解吗?

    多个线程不管以何种方式访问某个类,并且在主调代码中不需要进行同步,都能表现正确的行为. 线程安全有以下几种实现方式: 不可变 不可变(Immutable)的对象一定是线程安全的,不需要再采取任何的线程 ...

  10. make CMake 来龙去脉

    理论上说,任意一个C++程序都可以用g++来编译. 大家都知道,写程序大体步骤如下: 1.用编辑器编写源代码,如.c文件. 2.用编译器编译代码生成目标文件,如.o. 3.用链接器连接目标代码生成可执 ...