优化 DP 专题

( 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(); }
原文地址:https://www.cnblogs.com/lbn233/p/Better-DP.html