1. 程式人生 > >Codeforces Round #521 (Div. 3) F2 單調佇列dp

Codeforces Round #521 (Div. 3) F2 單調佇列dp

題意:給你n個有權值的點,你從位置0開始,每次操作可以跳到當前位置+k範圍內的點並獲得其權值(不能跳到原點),要求必須操作x次,且最後的位置+k必須要大於n,求可以獲得的最大權值。

思路:設d[ i ][ j ]為經過 j 次操作到達第 i 個位置所獲得的最大權值,方程很容易想到 d[ i ][ j ]=max(d[ p ][ j-1 ])+a[ i ],p+k>=i,但是這個方程複雜度是n*x*k,過不了這題,但是我們可以用單調遞減佇列去維護d[ p ][ j-1 ]的值,複雜度就降低成了n*k。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=5005;
int a[maxn];
ll d[maxn][maxn];
int q[maxn];
int main()
{
	int n,k,x;
	scanf("%d%d%d",&n,&k,&x);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]);
	if(k*x+k-1<n||x>n)
	{
		puts("-1");
		return 0;
	}
	ll ans=0;
	d[0][0]=1;
	for(int j=1;j<=x;j++)
	{
		int front=1,rear=0;
		q[++rear]=j-1;
		for(int i=j;i<=n;i++)
		{
			while(front<=rear&&q[front]+k<i)front++;
			if(front<=rear&&d[q[front]][j-1]!=0)
			d[i][j]=d[q[front]][j-1]+a[i];
			while(front<=rear&&d[q[rear]][j-1]<=d[i][j-1])rear--;
			q[++rear]=i;
			if(j==x&&i+k>n)
			ans=max(ans,d[i][j]);
		}
	}
	cout<<ans-1;
}