Link
神题。
手玩一下样例,发现重点肯定在这个顶部元素和 \(3\) 的大小之间的次序关系。考虑最特殊的,即对于 \(n\) 个三元组,都有如 \(A_{i, 1} \lt A_{i, 2} \lt A_{i , 3}\) 的形式,那么我们最终得到一个有序的序列。
接下来加一点干扰,比如对于 \(A_{j}\) 中出现了 \(A_{j, 1} \lt A_{j, 2} \gt A_{j, 3}\) 或者 \(A_{j, 1} \gt A_{j, 2} \gt A_{j, 3}\) 的情况,当 \(A_{j, 1}\) 被弹出的时候,我们有 \(\min_{1 \leq i \leq n, i \neq j} A_i \gt A_{j, 1}\) 与 \(A_{j, 2} \gt A_{j, 3}\),也就是说 \(A_{j}\) 中元素会呈现一个紧贴的趋势。由此可以得到非常重要的三个限制:
- 一个主元素(即紧贴元素串的头)会和 \([0, 2]\) 个元素紧贴
- 一个主元素后的紧贴元素都小于它自身
- 独立的主元素个数不少于紧贴 \(1\) 个数的主元素个数
Another key observation 是,主元素在答案排列中必定大于前面所有数(前缀 \(\max\)),这个完全可以通过写几组三元组和答案序列发现。那么我们要考虑的就是怎么排列这些主元素,再将其方案数乘上紧贴的元素个数的排列方案数即可。换句话说,最终排列有:
- 等价于若干个主元素带紧贴串,按照主元素大小从小到大排序
- 独立的主元素个数不少于大小为 \(2/3\) 的紧贴串
怎么构造答案呢?从 \(A = \{ 1, 2, 3, \dots, 3n \}\) 中,不断将集合中的剩余元素中最大值调出来作为主元素,同时我们分割出紧贴数,并为其配上相应的紧贴串。
有一个不是很自然的 DP 用于计算这个过程,记 \(f(i, j)\) 表示剩下 \(i\) 个可分配数,独立主元素比紧贴一元素的子元素多 \(j\) 个,但是转移时显然的:
- 独立出最大值,集合中剩下的 \(i - 1\) 个数进入下一轮
- 独立出最大值,从集合中 \(i - 1\) 个数中挑一个作为子元素,集合中剩下 \(i - 2\) 个数进入下一轮
- 独立出最大值,从集合中 \(i - 2\) 个数中挑两个作为子元素,集合中剩下 \(i - 3\) 个数进入下一轮
统计时注意方向。
复杂度是 \(O(n^2)\) 的。
#include <bits/stdc++.h>using i64 = long long;constexpr int N = 6007;
constexpr int M = 9007;int n, m;
i64 ans;
i64 dp[N][M];int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);std::cin >> n >> m;const int P = m;dp[n * 3][3000] = 1;for (int i = 3 * n; i; i--) {for (int k = 0; k <= 9000; k++) {if (dp[i][k]) {for (int c = 1; c <= 3; c++) {if (c <= i) {if (c == 1)dp[i - 1][k + 1] = (dp[i - 1][k + 1] + dp[i][k]) % P;if (c == 2)dp[i - 2][k - 1] = (dp[i - 2][k - 1] + dp[i][k] * (i - 1)) % P;if (c == 3)dp[i - 3][k] = (dp[i - 3][k] + dp[i][k] * (i - 1) * (i - 2)) % P;}}}}}for (int i = 3000; i <= 9000; i++) {ans = (ans + dp[0][i]) % P;}std::cout << ans << "\n";return 0;
}