题解
首先向考虑小范围的数据。
\(a[i]\)表示前\(i\)个球的总能量,一个前缀和很好理解的
\(f[i]\)表示获得第\(i\)个球后的总能量
呢么很显然我们可以\(DP\)了!!
f[i] = max(f[j] + a[i]-a[j] - 100*i)
呢么这就是一个\(O(n^2)\)的算法。很显然过不了。
呢么自然而然就是优化
常见的优化就是压维。
好吧,单调性优化
我们发现查找\(j\)是很费时的,我们可不可以用\(O(1)\)的办法把\(j\)找出来呢???
首先我们用一个队列来储存\(j\)的最优值,每次取队头就好了
呢么我们怎么更新队列呢
队头简单如果\(f[j]\)的能量小于当前\(i\)的高度更新掉就好了
队尾比较难理解先看代码
while(f[i] - a[i] > f[q[r]] - a[q[r]]) r--;
\(f[i] - a[i]\) 就是已经获得\(i\)的能量后,损失能量的总和的相反数
很自然\(i\)损失的能量小于\(q[r]\)的能量肯定要舍弃队尾
因为是相反数,所以要变成大于(不等式的性质,不等式两侧同乘一个负数,不等式要变号)
coding
#include <bits/stdc++.h>
using namespace std;
const int N = 2000005;
int n,f[N] = {},a[N] = {};
int cnt,ans = 0,q[N] = {},l = 0,r = 1;
inline int read()
{
register int x = 0;
register char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9')
{
x = (x<<3)+(x<<1) + ch-'0';
ch = getchar();
}
return x;
}
int main()
{
n = read(); f[0] = read();
for(register int i = 1;i <= n;i++) a[i] = a[i-1] + read();
for(register int i = 1;i <= n;i++)
{
while(f[q[l]] < 100*i && l < r) l++;
f[i] = f[q[l]] + a[i]-a[q[l]] - 100*i;
while(f[i] - a[i] > f[q[r]] - a[q[r]]) r--;
q[++r] = i;
}
printf("%d\n",f[n]);
return 0;
}