( ext{Example 1 : CF372C Watching Fireworks is Fun}) : 题目大意:城镇中有 个位置,有 (n) 个烟花要放。第 (i) 个烟花放出的时间记为 (t_i),放出的位置记为 (a_i)。如果烟花放出的时候,你处在位置 (x),那么你将收获 (b_i-|a_i-x|) 点快乐值。
初始你可在任意位置,你每个单位时间可以移动不大于 (d) 个单位距离。现在你需要最大化你能获得的快乐值。
首先很显然有一个 (N^2M) 的 DP,枚举 (i,j,k),(dp_{i,j}) 表示放第 (i) 个烟花时在位置 (j) 获得的最大快乐值,(k) 表示的是上一个烟花放时,人所在的位置。
那么显然有 (dp_{i,j}=max{dp_{i-1,k}+ b_i - |a_i - j|})。
然后因为枚举 (i,j) 在前面,所以 (i,j) 有关的全部提出来。
(dp_{i,j}=max{dp_{i-1,k}}+b_i-|a_i-j|) 就是得到的式子。
然后就可以队列维护状态最大值然后转移了。
如果你硬要拿 splay,线段树那我也没办法 ...
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1.5e5 + 10;
const int M = 3e2 + 10;
template <typename T> inline void read(T &ret) {
ret = 0; char ch = getchar();
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) ret = (ret<<1) + (ret<<3) + (ch^48), ch = getchar();
}
template <typename T, typename ... Args>
inline void read(T &x, Args &... args) { read(x), read(args...); }
int n, m, d, res = -1e18, que[N], dp[2][N];
struct sect { int a, b, t; } p[M];
inline void init() {
memset(dp, 0xcf, sizeof dp);
memset(que, 0, sizeof que);
for (int i = 1; i <= n; ++i) dp[0][i] = 0;
}
inline void solve() {
read(n, m, d); init();
for (int i = 1; i <= m; ++i)
read(p[i].a), read(p[i].b), read(p[i].t);
for (int i = 1; i <= m; ++i) {
int l = 1, r = 0, pos = 1, cur = i & 1;
for (int j = 1; j <= n; ++j) {
for (; pos <= min(n, j + d * (p[i].t - p[i - 1].t)); ++pos) {
while (l <= r && dp[cur ^ 1][que[r]] <= dp[cur ^ 1][pos]) --r;
que[++r] = pos;
}
while (l <= r && que[l] < max(j - d * (p[i].t - p[i - 1].t), 1LL)) ++l;
dp[cur][j] = dp[cur ^ 1][que[l]] - abs(p[i].a - j) + p[i].b;
}
}
for (int i = 1; i <= n; ++i) res = max(res, dp[m & 1][i]);
printf("%lld
", res); return ;
}
signed main() { solve(); }