\(\text{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\n", res); return ;
}
signed main() { solve(); }