1. 程式人生 > >【DP】WYF的遊戲

【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]);
}