【DP】WYF的游戏

【题目描述】

WYF从小就爱乱顶,但是顶是会造成位移的。
他之前水平有限,每次只能顶出k的位移,也就是从一个整点顶到另一个整点上。
我们现在将之简化到数轴上,即从一个整点可以顶到与自己相隔在k之内的数轴上的整点上。
现在WYF的头变多了,于是他能顶到更远的地方,他能顶到任意整点上。
现在他在玩一个游戏,这个游戏里他只能向正方向顶,同时如果他从i顶到j, 他将得到a[j]×(j?i)的分数。
其中a[j]是j点上的分数,且要求j>i,他最后必须停在n上。

【输入格式】

第一行一个整数n。
第二行有n个整数,其中第i个数表示a[i]。

【输出格式】

输出仅一个整数,表示WYF最多能得到的分数。


f[i]=f[j]+(i-j)*a[i];\\ f[k]+(i-k)*a[i]>f[k2]+(i-k2)*a[i];\\ f[k]+i*a[i]-k*a[i]>f[k2]+i*a[i]-k2*a[i];\\ f[k]-k*a[i]>f[k2]-k2*a[i];\\ f[k]-f[k2]>k*a[i]-k2*a[i];\\ f[k]-f[k2]>a[i]*(k-k2);\\ (f[k]-f[k2])/(k-k2)<a[i];

显然是标准的斜率优化,但因为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]);
}

猜你喜欢

转载自blog.csdn.net/Dy_Dream/article/details/84865649