【题目描述】
WYF从小就爱乱顶,但是顶是会造成位移的。
他之前水平有限,每次只能顶出k的位移,也就是从一个整点顶到另一个整点上。
我们现在将之简化到数轴上,即从一个整点可以顶到与自己相隔在k之内的数轴上的整点上。
现在WYF的头变多了,于是他能顶到更远的地方,他能顶到任意整点上。
现在他在玩一个游戏,这个游戏里他只能向正方向顶,同时如果他从i顶到j, 他将得到a[j]×(j?i)的分数。
其中a[j]是j点上的分数,且要求j>i,他最后必须停在n上。
【输入格式】
第一行一个整数n。
第二行有n个整数,其中第i个数表示a[i]。
【输出格式】
输出仅一个整数,表示WYF最多能得到的分数。
显然是标准的斜率优化,但因为a[i]不一定,斜率优化不符合单调性
以前被我们扔掉过的东西以后是可能还会有用的,所以我们此时就不能仅仅只用队列,因为如果每次返回去捡起的话,复杂度无法承担
那么我们考虑通过二分来解决这一类的问题,我们去队列中寻找(f[k]-f[k2])/(k-k2)<a[i]这个式子的上界,这其实就相当于我们跑了一次单调的斜率优化,前面被扔掉了而已
注意我们维护的是一个单调不上升序列
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
int a[100005],n,q[100005],f[100005],head=1,tail=1;
double g(int k,int k2)
{
return double(f[k]-f[k2])/(k-k2);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
int l=head,r=tail;
while(l<r)
{
int mid=(l+r)>>1;
if(g(q[mid],q[mid+1])>a[i]) l=mid+1;
else r=mid;
}
f[i]=f[q[l]]+(i-q[l])*a[i];
while(head<tail&&g(q[tail-1],q[tail])<g(q[tail],i)) tail--;
q[++tail]=i;
}
printf("%d",f[n]);
}