ABC313H

vjudge

标签 插入 DP

先考虑如果给了一个 \(a\),排列 \(b\) 的最优策略是啥?

其实就是令 \(c_i = \min(a_i, a_{i - 1})(a_0 = a_{n + 1} = -\infty)\) ,然后 \(c_i, b_i\) 都从大到小排序,所有 \(c_i < b_i\) 即可。

这提示我们要按 \(c, b\) 从大到小的顺序考虑。于是我们将 \(a, b\) 都从大到小排序,依次插入 \(a_i\)。(从小到大也行。)

值得注意的一点就是:我们把 \(a_i\) 插入 \(x\),原本的 \(c_x\) 就不见了,也就是说两个数之间是否还会有数插入是有影响的,于是我们可以设计出一个和常规插入 DP 看起不太一样的状态(下文中连续段就是指中间不会再插入元素):

\(f(i, j)\) 表示填了 \(a_{1} \sim a_i\),有 \(j\) 个连续段的方案数。根据这个也可以确定出现在已经有 \(i - j\)\(c\) 确定了,方便与 \(b\) 比较。

转移分三种类型:

  • \(a_i\) 自成一段,\(f(i, j) \leftarrow jf(i - 1, j - 1)\)

  • \(a_i\) 插到某个连续段的开头/结尾,那么 \(c_{i - j} = a_i\),需满足 \(a_i < b_{i - j}\) 则有转移 \(f(i, j) \leftarrow 2jf(i - 1, j)\)

  • \(a_i\) 插入到两个连续段中间,把两个段合并起来,那么 \(c_{i - j} = c_{i - j - 1} = a_i\),需满足 \(a_i < b_{i - j}\) 则有转移 \(f(i, j) \leftarrow jf(i - 1, j + 1)\)

初始状态:\(f(2, 2) = 1\)(插入 \(a_0, a_{n + 1}\)),目标状态:\(f(n + 2, 1)\),时间复杂度:\(O(n^2)\)

顺嘴提一句从小到大的排序方式,是差不多的,状态一样,只是确定了 \(i + j\)\(c\)(每段两端的也确定了)。初始状态是 \(f(0, 0) = 1\),目标状态是 \(f(n, 1)\)。本质是一样的,差别是一个段的两端的 \(c\) 是否确定了。

  • 第一种转移,\(c_{i + j - 1} = c_{i + j} = a_i\),要求 \(a_i < b_{i + j - 1}\)

  • 第二种,\(c_{i + j} = a_i\),要求 \(a_i < b_{i + j}\)

  • 第三种没有 \(c\) 新确定。

总结:先确定好最优策略,提示我们根据 \(b, c\) 值的顺序进行插入,发现两个数之间是否还有数很重要,设计状态转移即可。

虽然我代吗写的是从小到大排序。

#include <bits/stdc++.h>

using namespace std;

const int MAXN = 5005, Mod = 998244353;

int n, a[MAXN], b[MAXN], dp[MAXN][MAXN];

int main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }
  for (int i = 1; i <= n + 1; i++) {
    cin >> b[i];
  }
  sort(a + 1, a + n + 1);
  sort(b + 1, b + n + 2);
  dp[0][0] = 1;
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= i; j++) {
      dp[i][j] = 1ll * j * dp[i - 1][j + 1] % Mod; // 合并两段
      if (i + j <= n + 1){
        if (a[i] < b[i + j]) { // 加载一个段的开头、末尾
          dp[i][j] = (dp[i][j] + 2ll * j * dp[i - 1][j]) % Mod;
        }
        if (a[i] < b[i + j - 1]) { // 自成一段
          dp[i][j] = (dp[i][j] + 1ll * j * dp[i - 1][j - 1]) % Mod;
        }
      }
      //cout << dp[i][j] << ' ';
    }
    //cout << '\n';
  }
  cout << dp[n][1];
  return 0;
}
posted @ 2025-12-09 10:31  xiehanrui0817  阅读(9)  评论(0)    收藏  举报