题意:有一个小镇,有1~n共 n 处位置,有 m 个烟花表演,第 i 个烟花会在第 ti 时间绽放,绽放位置为 ai,你在每个时刻会在某个位置 w,当第 i 个烟花绽放时,你处在位置 w,你会得到 bi-|ai-w| 的高兴值,你每个单位时间可移动0 ~ d个长度,求 m 场烟花表演后最大的高兴值和最大为多少
定义d[i][j]为第i场烟花在位置j时的最大高兴值和为多少
显然d[i][j]由d[i-1][k]的max转移过来,k为 j-(t[i]-t[i-1])*d ~ j+(t[i]-t[i-1])*d
因此用一个单调队列维护d[i-1]的k区间的最大值
代码:
#include <bits/stdc++.h>
#define ll long long
#define IOS std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
template<class T> inline void read(T &x){
x=0; register char c=getchar(); register bool f=0;
while(!isdigit(c))f^=c=='-',c=getchar();
while(isdigit(c))x=x*10+c-'0',c=getchar(); if(f)x=-x;
}
using namespace std;
ll n,m,d,dis,op,val,mdx;
ll a[505],b[505],t[505];
ll f[150002];
ll savemax[250005];
ll qmax[250005];
void pp()
{
ll ans=-999999999999999999;
for(int j=1;j<=n;j++)
{ans=max(ans,f[j]);}
cout<<ans<<endl;
}
void RMQ(ll dis)//单调队列维护f(i-1)定长区间最值
{
if(dis==0)
{
for(int j=1;j<=n;j++)
{savemax[j]=f[j];}
return;
}
ll ds=1,dw=0;
for(int j=1;j<=n;j++)
{
while(ds<=dw&&f[j]>=f[qmax[dw]])
{dw--;}
qmax[++dw]=j;
if(j>=dis)
{
while(qmax[ds]<=(j-dis)){ds++;}
savemax[j]=f[qmax[ds]];
}
}
if(dis>n){dis=n+1;}
ll maxx=f[1];
savemax[1]=maxx;
for(int j=2;j<dis;j++)
{
if(f[j]>maxx)
{maxx=f[j];}
savemax[j]=maxx;
}
}
int main()
{
IOS
cin>>n>>m>>d;
for(int i=1;i<=m;i++)
{cin>>a[i]>>b[i]>>t[i];}
for(int j=1;j<=n;j++)
{f[j]=b[1]-abs(a[1]-j);}
if(m==1){pp();return 0;}//只有一场烟花
dis=(t[2]-t[1])*d+1;
RMQ(dis);
for(ll i=2;i<=m;i++)
{
op=(t[i]-t[i-1])*d;
for(ll j=1;j<=n;j++)
{
val=b[i]-abs(a[i]-j);
mdx=min(j+op,n);
f[j]=max(savemax[mdx],savemax[j])+val;
}
if(i==m){pp();break;}
dis=(t[i+1]-t[i])*d+1;
RMQ(dis);
}
return 0;
}