题意
在
n
n
个点的街道上,将有
m
m
个烟花要放。每个烟花拥有三个参数
a,b,t
a
,
b
,
t
。
t
t
表示该烟花在t时刻放。
假设现在时间正好为
t
t
时刻,一个人站在
x
x
点位置则他获得的开心值为
bi−|ai−x|
b
i
−
|
a
i
−
x
|
现在有一个人起始可以站在任意地方,他每单位时间可以移动d。询问最后他看完所有烟花最多能得到多少开心值。
现
在
有
一
个
人
起
始
可
以
站
在
任
意
地
方
,
他
每
单
位
时
间
可
以
移
动
d
。
询
问
最
后
他
看
完
所
有
烟
花
最
多
能
得
到
多
少
开
心
值
。
n,m,d(1≤n≤150000;1≤m≤300;1≤d≤n)
n
,
m
,
d
(
1
≤
n
≤
150000
;
1
≤
m
≤
300
;
1
≤
d
≤
n
)
ai,bi,ti(1≤ai≤n;1≤bi≤109;1≤ti≤109)
a
i
,
b
i
,
t
i
(
1
≤
a
i
≤
n
;
1
≤
b
i
≤
10
9
;
1
≤
t
i
≤
10
9
)
ti≤ti+1
t
i
≤
t
i
+
1
题解
Step1
S
t
e
p
1
dp[i][j]表示说前看了前i个烟花走到j位置时获得的最多快乐值(1≤i≤m;1≤j≤n)
d
p
[
i
]
[
j
]
表
示
说
前
看
了
前
i
个
烟
花
走
到
j
位
置
时
获
得
的
最
多
快
乐
值
(
1
≤
i
≤
m
;
1
≤
j
≤
n
)
那么考虑上一个烟花在k位置的他获得的最大值,即dp[i−1][k]。于是状态可以如下转移
那
么
考
虑
上
一
个
烟
花
在
k
位
置
的
他
获
得
的
最
大
值
,
即
d
p
[
i
−
1
]
[
k
]
。
于
是
状
态
可
以
如
下
转
移
dp[i][j]=max(dp[i][j],dp[i−1][k]+b[i]−|a[i]−x|)
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
j
]
,
d
p
[
i
−
1
]
[
k
]
+
b
[
i
]
−
|
a
[
i
]
−
x
|
)
当然需要注意k的范围j−d∗(ti−ti−1)≤k≤j+d∗(ti−ti−1)
当
然
需
要
注
意
k
的
范
围
j
−
d
∗
(
t
i
−
t
i
−
1
)
≤
k
≤
j
+
d
∗
(
t
i
−
t
i
−
1
)
可以写出这样的代码
ll dp[330][150010];
for(int i=1;i<=m;i++) {
int t = fire[i].t - fire[i-1].t;
for(int j=1;j<=n;j++) {
dp[i][j] = -inf;
for(int k=max(1,j-t*d);k<=min(n,j+t*d);k++) {
dp[i][j] = max(dp[i][j],dp[i-1][k] + fire[i].b - abs(fire[i].a-j));
}
}
}
不幸的是内存超了。
Step2
S
t
e
p
2
发现
dp[i][j]只和dp[i−1][k]有关系利用滚动数组优化即可
d
p
[
i
]
[
j
]
只
和
d
p
[
i
−
1
]
[
k
]
有
关
系
利
用
滚
动
数
组
优
化
即
可
写出如下代码
for(int i=1;i<=m;i++) {
int t = fire[i].t - fire[i-1].t;
for(int j=1;j<=n;j++) {
dp[i&1][j] = -inf;
for(int k=max(1,j-t*d);k<=min(n,j+t*d);k++) {
dp[i&1][j] = max(dp[i&1][j],dp[!(i&1)][k] + fire[i].b - abs(fire[i].a-j));
}
}
}
内存是优化了不少,时间上却超时了。
Step3
S
t
e
p
3
dp[i][j]=max(dp[i][j],dp[i−1][k]+b[i]−|a[i]−x|)
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
j
]
,
d
p
[
i
−
1
]
[
k
]
+
b
[
i
]
−
|
a
[
i
]
−
x
|
)
j−d∗(ti−ti−1)≤k≤j+d∗(ti−ti−1)
j
−
d
∗
(
t
i
−
t
i
−
1
)
≤
k
≤
j
+
d
∗
(
t
i
−
t
i
−
1
)
我们注意到上式是一个区间最值问题,且满足划窗性,于是考虑单调队列优化。
dp[i][j]=max(dp[i−1][k])+b[i]−|a[i]−x|
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
−
1
]
[
k
]
)
+
b
[
i
]
−
|
a
[
i
]
−
x
|
单调队列维护
dp[i−1][k]
d
p
[
i
−
1
]
[
k
]
的最大值即可。
最终版本代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,ll> node;
const int maxn = 330;
const ll inf = 1LL<<62;
int n,m;
ll d;
struct Fire {
int a;
ll b,t;
bool operator < (const Fire &a) const {
return t < a.t;
}
};
node qu[450010];
Fire fire[maxn];
ll dp[2][150010];
int main()
{
while(~scanf("%d%d%lld",&n,&m,&d)) {
fire[0].t = 0;
for(int i=1;i<=m;i++) scanf("%d%lld%lld",&fire[i].a,&fire[i].b,&fire[i].t);
sort(fire+1,fire+1+m);
memset(dp,0,sizeof(dp));
for(int i=1;i<=m;i++) {
ll t = fire[i].t - fire[i-1].t;
int l = 1,r = 0;
for(int j=1;j<=n;j++) {
dp[i&1][j] = -inf;
/// 维护一个单调递减的队列
while(l <= r && qu[r].second < dp[!(i&1)][j]) r--;
qu[++r] = make_pair(j,dp[!(i&1)][j]);
if(j > t*d) {
int pos = j - t*d;
/// 排除位置不满足的最大值
while(l <= r && qu[l].first + d*t < pos) l++;
if(l <= r) dp[i&1][pos] = max(dp[i&1][pos],qu[l].second + fire[i].b - abs(fire[i].a-pos));
}
}
/// 有些位置还没有更新到
for(int j=max(1ll*n-t*d+1,1ll);j<=n;j++) {
while(l <= r && qu[l].first + d*t < j) l++;
if(l <= r) dp[i&1][j] = max(dp[i&1][j],qu[l].second + fire[i].b - abs(fire[i].a-j));
}
}
ll res = -inf;
for(int i=1;i<=n;i++) res = max(res,dp[m&1][i]);
printf("%lld\n",res);
}
}