题解:AtCoder AT_awc0044_e Restoration of Compressed Number Sequence
【题目来源】
AtCoder:E - Restoration of Compressed Number Sequence
【题目描述】
Takahashi places \(N\) balls numbered from \(1\) to \(N\) into some of \(M\) boxes numbered from \(1\) to \(M\), in the order of ball \(1\), ball \(2\), \(\dots\), ball \(N\). Each box can hold any number of balls. Let \(B_i\) denote the box number in which ball \(i\) is placed (\(1 \leq B_i \leq M\)).
高桥将编号为 \(1\) 到 \(N\) 的 \(N\) 个球按顺序(球 \(1\),球 \(2\),…,球 \(N\))放入编号为 \(1\) 到 \(M\) 的若干个盒子中。每个盒子可以容纳任意数量的球。设 \(B_i\) 表示放置球 \(i\) 的盒子编号(\(1 \leq B_i \leq M\))。
Aoki recorded the box number sequence \(B_1, B_2, \dots, B_N\) by converting it into a "compressed number sequence" \(C_1, C_2, \dots, C_N\) using the following procedure. This procedure assigns new numbers \(1, 2, 3, \dots\) in the order of first appearance of each box number.
青木通过以下过程将盒子编号序列 \(B_1, B_2, \dots, B_N\) 转换为"压缩编号序列" \(C_1, C_2, \dots, C_N\) 并记录下来。这个过程为每个盒子编号按其首次出现的顺序分配新的编号 \(1, 2, 3, \dots\)。
> Initially, no box number has a compressed number assigned to it.
初始时,没有为任何盒子编号分配压缩编号。
>
> For \(i = 1, 2, \dots, N\) in order, do the following:
按顺序对 \(i = 1, 2, \dots, N\) 执行以下操作:
> - If box number \(B_i\) has not yet been assigned a compressed number (i.e., \(B_i\) is different from all of \(B_1, B_2, \dots, B_{i-1}\)), assign to box number \(B_i\) the smallest positive integer not yet assigned to any box number as its compressed number. Set \(C_i\) to that compressed number.
如果盒子编号 \(B_i\) 尚未被分配压缩编号(即 \(B_i\) 与 \(B_1, B_2, \dots, B_{i-1}\) 中的所有元素都不同),则将尚未分配给任何盒子编号的最小正整数作为压缩编号分配给盒子编号 \(B_i\)。将 \(C_i\) 设为该压缩编号。
> - If box number \(B_i\) has already been assigned a compressed number (i.e., there exists some element among \(B_1, B_2, \dots, B_{i-1}\) equal to \(B_i\)), set \(C_i\) to the compressed number already assigned to box number \(B_i\).
如果盒子编号 \(B_i\) 已经被分配了压缩编号(即 \(B_1, B_2, \dots, B_{i-1}\) 中存在某个元素等于 \(B_i\)),则将 \(C_i\) 设为已分配给盒子编号 \(B_i\) 的压缩编号。
For example, when the box number sequence is \((5, 5, 2, 7, 2)\), box number \(5\) is assigned compressed number \(1\), box number \(2\) is assigned compressed number \(2\), and box number \(7\) is assigned compressed number \(3\), resulting in the compressed number sequence \((1, 1, 2, 3, 2)\).
例如,当盒子编号序列为 \((5, 5, 2, 7, 2)\) 时,盒子编号 \(5\) 被分配压缩编号 \(1\),盒子编号 \(2\) 被分配压缩编号 \(2\),盒子编号 \(7\) 被分配压缩编号 \(3\),从而得到压缩编号序列 \((1, 1, 2, 3, 2)\)。
However, part of the record became dirty and unreadable.
但是,部分记录变得污损而无法辨认。
You are given a sequence \(D_1, D_2, \dots, D_N\) of length \(N\). When \(D_i = 0\), the value of \(C_i\) is unknown. When \(D_i \neq 0\), it must hold that \(C_i = D_i\).
给定一个长度为 \(N\) 的序列 \(D_1, D_2, \dots, D_N\)。当 \(D_i = 0\) 时,\(C_i\) 的值未知。当 \(D_i \neq 0\) 时,必须满足 \(C_i = D_i\)。
Find the number of box number sequences \(B_1, B_2, \dots, B_N\) (where each \(B_i\) is an integer between \(1\) and \(M\) inclusive) such that the compressed number sequence \(C_1, C_2, \dots, C_N\) obtained by the above procedure satisfies all the conditions of \(D\), modulo \(998244353\). Here, two box number sequences are considered distinct if there exists some \(i\) such that their values of \(B_i\) differ. If no valid box number sequence exists, output \(0\).
求使得通过上述过程得到的压缩编号序列 \(C_1, C_2, \dots, C_N\) 满足 \(D\) 的所有条件的盒子编号序列 \(B_1, B_2, \dots, B_N\) 的数量(其中每个 \(B_i\) 是 \(1\) 到 \(M\) 之间的整数),结果对 \(998244353\) 取模。这里,如果两个盒子编号序列在某个 \(i\) 处的 \(B_i\) 值不同,则认为它们是不同的。如果不存在有效的盒子编号序列,则输出 \(0\)。
【输入】
\(N\) \(M\)
\(D_1\) \(D_2\) \(\dots\) \(D_N\)
- The first line contains the number of balls \(N\) and the maximum box number \(M\), separated by a space.
- The second line contains \(D_1, D_2, \dots, D_N\), representing the partially unknown compressed number sequence, separated by spaces.
- When \(D_i = 0\), it means the \(i\)-th compressed number is unknown.
- When \(D_i \geq 1\), it means the \(i\)-th compressed number is \(D_i\).
【输出】
Output the number of valid box number sequences modulo \(998244353\) in a single line.
【输入样例】
5 4
1 1 2 0 2
【输出样例】
48
【核心思想】
-
问题分析:给定 \(N\) 个球和 \(M\) 个盒子,每个球放入一个盒子形成序列 \(B\)。压缩序列 \(C\) 按首次出现顺序分配新编号。给定部分已知的 \(D\) 序列(\(D_i=0\) 表示未知),求满足条件的 \(B\) 序列数量。这是一个线性DP计数问题,关键在于用已使用颜色数量作为状态。
-
算法选择:
- 线性DP:
dp[j]表示处理到当前位置,已使用 \(j\) 种不同颜色的方案数 - 状态压缩:不记录具体颜色分配,只记录颜色数量
- 组合计数:利用乘法原理计算选择方案数
- 线性DP:
-
关键步骤:
- 初始化:
dp[0] = 1,表示初始未使用任何颜色 - 状态转移(对于每个位置,根据 \(D_i\) 的值):
- \(D_i = 0\)(未知):
- 使用已有颜色:
ep[j] += dp[j] * j(\(j\) 种选择) - 使用新颜色:
ep[j+1] += dp[j] * (m-j)(\(m-j\) 种选择)
- 使用已有颜色:
- \(D_i \leq j\)(已有颜色):
ep[j] += dp[j](必须选已有颜色之一) - \(D_i = j+1\)(新颜色):
ep[j+1] += dp[j] * (m-j)(必须选新颜色)
- \(D_i = 0\)(未知):
- 统计答案:
ans = sum(dp[j]),对所有可能的颜色数求和
- 初始化:
-
时间/空间复杂度:
- 时间复杂度:\(O(N \times \min(N, M))\)
- 空间复杂度:\(O(\min(N, M))\)
-
线性DP状态压缩计数的核心思想:
- 状态抽象:用颜色数量代替具体分配,大幅降低状态空间
- 乘法原理:选择已有颜色有 \(j\) 种方案,新颜色有 \(m-j\) 种方案
- 约束分类:根据 \(D_i\) 的三种情况分别处理转移
- 适用于带约束的序列计数问题
【算法标签】
线性DP-一维
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 200005, MOD = 998244353;
int n, m; // n: 序列长度, m: 颜色种类数
int d[N]; // 序列元素
int dp[N], ep[N]; // dp: 动态规划数组
signed main()
{
cin >> n >> m; // 输入序列长度和颜色数
int siz = min(n, m); // 可能的不同颜色数不会超过n和m的最小值
dp[0] = 1; // 初始状态:没有使用颜色
for (int i = 1; i <= n; i++) // 处理序列的每个元素
{
int d;
cin >> d; // 输入当前元素
// 初始化临时数组
for (int j = 0; j <= siz; j++)
{
ep[j] = 0;
}
// 状态转移
for (int j = 0; j <= siz; j++) // j表示当前使用了j种不同颜色
{
if (dp[j] == 0) continue; // 无效状态
if (d == 0) // 当前元素未确定
{
// 情况1:使用已有的j种颜色中的一种
ep[j] = (ep[j] + dp[j] * j) % MOD;
// 情况2:使用一种新的颜色
if (j != siz) // 还有新的颜色可用
{
ep[j + 1] = (ep[j + 1] + dp[j] * (m - j)) % MOD;
}
}
else if (d <= j) // 当前元素是已有颜色之一
{
ep[j] = (ep[j] + dp[j]) % MOD;
}
else if (d == j + 1) // 当前元素是一种新颜色
{
if (j != siz) // 还有新的颜色可用
{
ep[j + 1] = (ep[j + 1] + dp[j] * (m - j)) % MOD;
}
}
}
// 更新dp数组
for (int j = 0; j <= siz; j++)
{
dp[j] = ep[j];
}
}
// 计算最终答案
int ans = 0;
for (int j = 0; j <= siz; j++)
{
ans = (ans + dp[j]) % MOD;
}
cout << ans << endl;
return 0;
}
【运行结果】
5 4
1 1 2 0 2
48
浙公网安备 33010602011771号