【DP】WYF的遊戲
阿新 • • 發佈:2018-12-30
【題目描述】
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]); }