F2. Pictures with Kittens (hard version)
题意:给你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;
}