题目大意:
题目链接:https://jzoj.net/senior/#main/show/4249
一个长度为
n
n
n的数列,每次可以选择前面任意一个位置
i
i
i跳到那里,价值为
a
i
×
(
i
−
a_i\times(i-
ai×(i−你所在的位置
)
)
)。求跳到
n
n
n的最大价值。
思路:
证明:每次选择 m a x { a i ( i ∈ j ∼ n ) } max\{a_i(i\in j\sim n)\} max{ai(i∈j∼n)} 其中 j j j 表示你所在的位置。
先设起点是0。
那么假设 i i i是最大价值的点,但是最优解应该先跳到 j j j,且下一次将跳到 k k k,且 j < i < k j<i<k j<i<k。
那么从0跳到 j j j的价值是 j × a j j\times a_j j×aj,跳到 i i i的价值是 i × a i i\times a_i i×ai。
从 j j j跳到 k k k的价值是 ( k − j ) a k (k-j)a_k (k−j)ak,从 i i i跳到 k k k的价值是 ( k − i ) a k (k-i)a_k (k−i)ak。
明显从 j j j跳到 k k k的价值比从 i i i跳到 k k k的价值大 ( i − j ) a k (i-j)a_k (i−j)ak。
那么如果 i × a i − j × a j > ( i − j ) a k i\times a_i-j\times a_j>(i-j)a_k i×ai−j×aj>(i−j)ak,那么跳到 i i i是更合法的。
移项,只要 i × a i > ( i − j ) a k + j × a j i\times a_i>(i-j)a_k+j\times a_j i×ai>(i−j)ak+j×aj就更合法。
设 T = m a x ( a k , a j ) T=max(a_k,a_j) T=max(ak,aj),有 ( i − j ) a k + j × a j ≤ ( i − j ) T + j × T (i-j)a_k+j\times a_j\leq (i-j)T+j\times T (i−j)ak+j×aj≤(i−j)T+j×T
不等号右侧合并同类项得 ( i − j ) a k + j × a j ≤ ( i − j + j ) T (i-j)a_k+j\times a_j\leq (i-j+j)T (i−j)ak+j×aj≤(i−j+j)T,即 ( i − j ) a k + j × a j ≤ T i (i-j)a_k+j\times a_j\leq Ti (i−j)ak+j×aj≤Ti
因为 a i > T a_i>T ai>T(已经假设 i i i是最大价值的点),所以 i × a I > T i i\times a_I>Ti i×aI>Ti
所以有 i × a i > T i ≥ ( i − j ) a k + j × a j i\times a_i>Ti\geq (i-j)a_k+j\times a_j 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\n",ans);
return 0;
}