一、题目
二、解法
烟花和答案的计算有很大关系,而且烟花数也不多,尝试将其加入状态,所以定义
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]为考虑到第
i
i
i个烟花,现在位置在
j
j
j,所得到的最小
∣
a
i
−
j
∣
|a_i-j|
∣ai−j∣和(最后用
b
b
b减去它就得到了最大的开心值,这样更方便),转移:
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
k
]
+
∣
a
i
−
j
∣
dp[i][j]=dp[i-1][k]+|a_i-j|
dp[i][j]=dp[i−1][k]+∣ai−j∣这个转移要求
j
−
t
d
≤
k
≤
j
+
t
d
j-td\leq k\leq j+td
j−td≤k≤j+td,这是一个典型的滑动窗口问题,从前往后,从后往前跑两次单调队列即可,时间复杂度
O
(
n
m
)
O(nm)
O(nm),数组的第一维需要滚动优化。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define int long long
const int M = 150005;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,d,sum,ans,q[M],a[M],b[M],t[M],dp[2][M];
int Abs(int x)
{
return x>0?x:-x;
}
signed main()
{
n=read();m=read();d=read();
for(int i=1;i<=m;i++)
{
a[i]=read();b[i]=read();t[i]=read();
sum+=b[i];
}
for(int i=1;i<=n;i++) dp[1][i]=Abs(a[1]-i);
for(int i=2;i<=m;i++)
{
int p=(i-1)&1,ti=(t[i]-t[i-1])*d;
//memset(dp[i&1],0x3f,sizeof(dp[i&1]));
int h=1,tl=0;
for(int j=1;j<=n;j++)
{
while(h<=tl && q[h]<j-ti) h++;
while(h<=tl && dp[p][q[tl]]>=dp[p][j]) tl--;
q[++tl]=j;
dp[i&1][j]=dp[p][q[h]]+Abs(a[i]-j);
}
h=1,tl=0;
for(int j=n;j>=1;j--)
{
while(h<=tl && q[h]>j+ti) h++;
while(h<=tl && dp[p][q[tl]]>=dp[p][j]) tl--;
q[++tl]=j;
dp[i&1][j]=min(dp[i&1][j],dp[p][q[h]]+Abs(a[i]-j));
}
}
ans=-1e9;
for(int i=1;i<=n;i++)
ans=max(ans,sum-dp[m&1][i]);
printf("%lld\n",ans);
}