D. Token Removing

问题描述

定义一个整数序列 aa 是有效的,当且仅当对于所有 1in1 ≤ i ≤ n,都有 0aii0 ≤ a_i ≤ i

定义一个长度为 nn 的有效序列 aa 的权重 f(a)f(a)

  • 最初,在数轴上的闭区间 [1,n][1, n] 的每个整数点上放置一个令牌。
  • 按顺序执行 nn 次操作。在第 ii 次操作期间,如果 ai0a_i \neq 0,则在闭区间 [ai,i][a_i, i] 中移除一个尚未被移除的令牌;否则,不执行任何操作。
  • f(a)f(a) 是移除令牌的方法数。如果存在一个 tt,使得两种方式在第 tt 次操作中移除的令牌位置不同,则认为这两种方式是不同的。
  • 例如,f([0,2,1])=2f([0,2,1]) = 2,因为我们可以按顺序移除位置在 2,12,12,32,3 的令牌。

JT 给你两个整数 n,mn, m,并要求你找出所有 (n+1)!(n+1)! 个长度为 nn 的有效序列的权重之和。由于答案可能太大,请将其对 mm 取模后输出。

输入

每个测试包含多个测试用例。第一行是测试用例的数量 tt (1t10001 ≤ t ≤ 1000)。接下来是测试用例的描述。

每个测试用例的唯一一行包含两个整数 nnmm (1n50001 ≤ n ≤ 5000, 108m1.01×10910^8 ≤ m ≤ 1.01 × 10^9) — 有效序列的长度和模数。

保证所有测试用例的 n2n^2 的总和不超过 2.5×1072.5 × 10^7

输出

对于每个测试用例,输出一个整数 — 所有 (n+1)!(n+1)! 个长度为 nn 的有效序列的权重之和,对 mm 取模。

题解

目标是计算所有可能的有效序列 a 的权重 f(a) 之和,对 m 取模。权重 f(a) 是基于序列 a 移除令牌的方法数。

1. 核心思想:转换视角

解决方案没有遍历每个可能的序列 a 并计算其贡献,而是将问题反过来思考。我们提出了一个不同的问题:

对于一个特定的被移除的令牌位置集合 P,它对最终答案的总贡献是多少?

如果我们能为任何给定的集合 P 计算出这个值,那么我们就可以将这些贡献在所有可能的集合 P 上求和,从而得到最终答案。

2. 单个令牌集合 P 的贡献

假设我们决定移除一个位于位置 P=p0,p1,...,pk1P = {p_0, p_1, ..., p_{k-1}} 的特定 kk 个令牌集合。为方便起见,我们按降序对它们进行排序:np0>p1>...>pk11n ≥ p_0 > p_1 > ... > p_{k-1} ≥ 1

要移除这个集合 P,我们需要找到一个序列 aa 和一个相应的移除计划。这涉及两个主要部分:

2.1 选择 a[t]a[t] 的值

对于在某个步骤 tjt_j 被移除的每个令牌 pjp_j,条件是 a[tj]pjtja[t_j] ≤ p_j ≤ t_j。值 a[tj]a[t_j] 可以是 1 到 pjp_j 之间的任何整数。这为 a[tj]a[t_j] 提供了 pjp_j 种选择。对于所有 kk 个令牌,a 值的总选择数是乘积 p0×p1×...×pk1p_0 × p_1 × ... × p_{k-1}

2.2 分配移除步骤 tjt_j

我们需要为每个令牌 pjp_j 分配一个唯一的移除步骤 tjt_j,使得 tjpjt_j ≥ p_j

  • 对于最大的令牌 p0p_0,它可以在从 p0p_0nn 的任何步骤中被移除。有 np0+1n - p_0 + 1 个可用步骤。
  • 对于第二大的令牌 p1p_1,它可以在从 p1p_1nn 的任何步骤中被移除,除了已经被 p0p_0 占用的步骤。由于 p0>p1p_0 > p_1p0p_0 的步骤对于 p1p_1 来说总是一个有效(但不可用)的步骤。这留下了 (np1+1)1(n - p_1 + 1) - 1 种选择。
  • 推广来说,对于令牌 pjp_j(它是第 jj 大的,其中 jj 从 0 开始),有 npj+1n - p_j + 1 个潜在步骤。然而,其中 jj 个步骤已经被分配给了比 pjp_j 大的 jj 个令牌。这为 tjt_j 留下了 (npj+1j)(n - p_j + 1 - j) 种选择。

2.3 最终贡献公式

结合这两个部分,单个固定集合 P 的总贡献是:

Contribution(P)=(j=0k1pj)×(j=0k1(npj+1j))=j=0k1pj(npj+1j)\text{Contribution}(P) = \left(\prod_{j=0}^{k-1} p_j\right) \times \left(\prod_{j=0}^{k-1} (n - p_j + 1 - j)\right) = \prod_{j=0}^{k-1} p_j(n - p_j + 1 - j)

我们的新目标是计算这个值在所有可能的非空子集 P{1,2,...,n}P \subseteq \{1, 2, ..., n\} 上的和。

3. 动态规划解法

枚举所有 2n2^n 个子集 P 太慢了。我们可以使用动态规划来高效地计算这个和。关键是逐个元素地构建集合 P。

关键的洞察是按降序考虑元素,从 nn11

3.1 DP 状态定义

让我们定义我们的 DP 状态:

dp[i][k]\text{dp}[i][k] = 所有大小为 kk 的集合 PP 的贡献之和,其中所有选择的令牌都来自范围 i,i+1,...,n{i, i+1, ..., n}

3.2 DP 转移

我们通过从 nn 向下迭代到 11 来计算这个值。对于每个 ii,我们做出一个决定:

  1. 不将 ii 包含在我们的集合 P 中:如果我们不选择 ii,我们仍然必须选择 kk 个令牌,但现在它们都必须来自范围 i+1,...,n{i+1, ..., n}。这种情况的贡献之和根据定义是 dp[i+1][k]\text{dp}[i+1][k]

  2. ii 包含在我们的集合 P 中:如果我们选择 ii,我们需要从范围 i+1,...,n{i+1, ..., n} 中选择另外 k1k-1 个令牌。

    • 来自 i+1,...,n{i+1, ..., n} 的所有此类 k1k-1 个令牌集合的贡献之和是 dp[i+1][k1]\text{dp}[i+1][k-1]
    • 现在,我们将 ii 添加到这些集合中的每一个。由于 ii 小于 i+1,...,n{i+1, ..., n} 中的任何数,它将始终是新的 kk 元素集合中最小的元素。这意味着它的排名(从大到小,0-索引)将始终是 k1k-1
    • 因此,ii 的乘法因子是 i×(ni+1(k1))i × (n - i + 1 - (k-1))
    • 这种情况的总贡献是 dp[i+1][k1]×i×(ni+2k)\text{dp}[i+1][k-1] × i × (n - i + 2 - k)

3.3 DP 递推关系

结合这些情况,我们得到递推关系:

dp[i][k]=dp[i+1][k]+dp[i+1][k1]×i×(ni+2k)\text{dp}[i][k] = \text{dp}[i+1][k] + \text{dp}[i+1][k-1] × i × (n - i + 2 - k)

基本情况:

  • dp[n+1][0]=1\text{dp}[n+1][0] = 1 (表示选择空集的一种方式)
  • dp[n+1][k]=0\text{dp}[n+1][k] = 0 for k>0k > 0

最终答案: 最终答案是所有可能的集合大小 kk 的贡献之和,考虑从 1 到 nn 的所有数字。即 k=0ndp[1][k]\sum_{k=0}^{n} \text{dp}[1][k]

提交链接