AT AGC043D Merge Triplets 题解

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}\) 中元素会呈现一个紧贴的趋势。由此可以得到非常重要的三个限制:

  1. 一个主元素(即紧贴元素串的头)会和 \([0, 2]\) 个元素紧贴
  2. 一个主元素后的紧贴元素都小于它自身
  3. 独立的主元素个数不少于紧贴 \(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;
}
posted @ 2025-11-11 15:15  起汐Moe  阅读(0)  评论(0)    收藏  举报