\(\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(); }