题目大意:
题目链接:https://jzoj.net/senior/#main/show/4249
一个长度为n的数列,每次可以选择前面任意一个位置i跳到那里,价值为ai×(i−你所在的位置)。求跳到n的最大价值。
思路:
证明:每次选择max{ai(i∈j∼n)} 其中j 表示你所在的位置。
先设起点是0。
那么假设i是最大价值的点,但是最优解应该先跳到j,且下一次将跳到k,且j<i<k。
那么从0跳到j的价值是j×aj,跳到i的价值是i×ai。
从j跳到k的价值是(k−j)ak,从i跳到k的价值是(k−i)ak。
明显从j跳到k的价值比从i跳到k的价值大(i−j)ak。
那么如果i×ai−j×aj>(i−j)ak,那么跳到i是更合法的。
移项,只要i×ai>(i−j)ak+j×aj就更合法。
设T=max(ak,aj),有(i−j)ak+j×aj≤(i−j)T+j×T
不等号右侧合并同类项得(i−j)ak+j×aj≤(i−j+j)T,即(i−j)ak+j×aj≤Ti
因为ai>T(已经假设i是最大价值的点),所以i×aI>Ti
所以有i×ai>Ti≥(i−j)ak+j×aj
证毕。
那么直接单调队列记录最大值即可。
代码:
#include <cstdio>
#include <queue>
#define mp make_pair
using namespace std;
int n,ans,x,y,last;
deque<pair<int,int> > q;
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&x);
while (q.size()&&q.back().first<x) q.pop_back();
q.push_back(mp(x,i));
}
while (q.size())
{
x=q.front().first;
y=q.front().second;
q.pop_front();
ans+=x*(y-last);
last=y;
}
printf("%d
",ans);
return 0;
}