【JZOJ4249】游戏【贪心】

题目大意:

题目链接:https://jzoj.net/senior/#main/show/4249
一个长度为nn的数列,每次可以选择前面任意一个位置ii跳到那里,价值为ai×(ia_i\times(i-你所在的位置))。求跳到nn的最大价值。


思路:

证明:每次选择max{ai(ijn)}max\{a_i(i\in j\sim n)\} 其中jj 表示你所在的位置。

先设起点是0。
那么假设ii是最大价值的点,但是最优解应该先跳到jj,且下一次将跳到kk,且j<i<kj<i<k
那么从0跳到jj的价值是j×ajj\times a_j,跳到ii的价值是i×aii\times a_i
jj跳到kk的价值是(kj)ak(k-j)a_k,从ii跳到kk的价值是(ki)ak(k-i)a_k
明显从jj跳到kk的价值比从ii跳到kk的价值大(ij)ak(i-j)a_k
那么如果i×aij×aj>(ij)aki\times a_i-j\times a_j>(i-j)a_k,那么跳到ii是更合法的。
移项,只要i×ai>(ij)ak+j×aji\times a_i>(i-j)a_k+j\times a_j就更合法。
T=max(ak,aj)T=max(a_k,a_j),有(ij)ak+j×aj(ij)T+j×T(i-j)a_k+j\times a_j\leq (i-j)T+j\times T
不等号右侧合并同类项得(ij)ak+j×aj(ij+j)T(i-j)a_k+j\times a_j\leq (i-j+j)T,即(ij)ak+j×ajTi(i-j)a_k+j\times a_j\leq Ti
因为ai>Ta_i>T(已经假设ii是最大价值的点),所以i×aI>Tii\times a_I>Ti
所以有i×ai>Ti(ij)ak+j×aji\times a_i>Ti\geq (i-j)a_k+j\times a_j
证毕。

那么直接单调队列记录最大值即可。


代码:

#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;
}
posted @ 2019-01-30 16:19  全OI最菜  阅读(98)  评论(0编辑  收藏  举报