题目大意
一个城镇有n个烟火点,从左到右编号为1~n,每个烟火点之间距离1个单位长度。节日中有m个烟火要放,给定放的地点
a
i
a_i
ai,时间
t
i
t_i
ti。如果点燃第i个烟花时你在烟火点x,你可以获得
b
i
−
∣
a
i
−
x
∣
b_i-|a_i-x|
bi−∣ai−x∣的开心值。
你每个单位时间可以移动不超过d个单位距离。你的初始位置是任意的(初始时刻为1),求你通过移动能获取到的最大的开心值。保证烟火按时间顺序从小到大给出
思路
显然根据套路,设
f
i
,
j
f_{i,j}
fi,j为正在放第i个烟火,在j观看的最大幸福值。
f
i
,
j
=
m
a
x
j
−
d
∗
(
t
i
−
t
i
−
1
)
<
=
k
<
=
j
+
d
∗
(
t
i
−
t
i
−
1
)
(
f
i
−
1
,
k
+
b
i
−
∣
a
i
−
j
∣
)
(
1
<
=
i
<
=
m
,
1
<
=
j
<
=
n
)
f_{i,j}=max_{j-d*(t_i-t_{i-1})<=k<=j+d*(t_i-t_{i-1})}(f_{i-1,k}+b_i-|a_i-j|)(1<=i<=m,1<=j<=n)
fi,j=maxj−d∗(ti−ti−1)<=k<=j+d∗(ti−ti−1)(fi−1,k+bi−∣ai−j∣)(1<=i<=m,1<=j<=n)
答案为
m
a
x
1
<
=
j
<
=
n
f
m
,
j
max_{1<=j<=n}f_{m,j}
max1<=j<=nfm,j
然后观察式子,发现3点:
·
b
i
b_i
bi可以提出来
· 可以使用滚动数组
· 显然需要使用单调队列
所以最后的方程再经过一点变化得到:
f
i
,
j
=
m
i
n
j
−
d
∗
(
t
i
−
t
i
−
1
)
<
=
k
<
=
j
+
d
∗
(
t
i
−
t
i
−
1
)
(
f
i
−
1
,
k
+
∣
a
i
−
j
∣
)
(
1
<
=
i
<
=
m
,
1
<
=
j
<
=
n
)
f_{i,j}=min_{j-d*(t_i-t_{i-1})<=k<=j+d*(t_i-t_{i-1})}(f_{i-1,k}+|a_i-j|)(1<=i<=m,1<=j<=n)
fi,j=minj−d∗(ti−ti−1)<=k<=j+d∗(ti−ti−1)(fi−1,k+∣ai−j∣)(1<=i<=m,1<=j<=n)
答案为
∑
i
=
1
m
b
i
−
m
i
n
1
<
=
j
<
=
n
f
m
,
j
\sum^m_{i=1}b_i-min_{1<=j<=n}f_{m,j}
∑i=1mbi−min1<=j<=nfm,j
使用单调队列优化即可。
code:
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
long long ans,f[2][150001],t[301],n,m,d,x,y,len,l,r,q[150001],u;
int main()
{
cin>>n>>m>>d;
for (long long i=1;i<=m;i++)
{
u=i%2;
cin>>x>>y>>t[u];
ans+=y;
len=(t[u]-t[u^1])*d;
l=1,r=0;
for (long long j=1;j<=n;j++)
{
while (l<=r&&q[l]<j-len) l++;
while (l<=r&&f[u^1][q[r]]>f[u^1][j]) r--;
q[++r]=j;
f[u][j]=f[u^1][q[l]]+abs(x-j);
}
l=1,r=0;
for (long long j=n;j>=1;j--)
{
while (l<=r&&q[l]>j+len) l++;
while (l<=r&&f[u^1][q[r]]>f[u^1][j]) r--;
q[++r]=j;
f[u][j]=min(f[u][j],f[u^1][q[l]]+abs(x-j));
}
}
for (long long i=1;i<=n;i++) f[m%2][1]=min(f[m%2][1],f[m%2][i]);
cout<<ans-f[m%2][1];
return 0;
}