Sequence
Sequence
贪心,集合,转化
题意
给定长度为 \(n\) 的 01 序列 \(s_1, s_2, \dots, s_n\),定义
其中 \([\,]\) 是艾弗森括号。
现在给定整数 \(n\) 以及多重集 \(\{p'_1, p'_2, \dots, p'_n\}\),它表示 \(\{p_1, p_2, \dots, p_n\}\) 的所有值构成的多重集。
求满足条件的 01 序列 \(s\) 的数量,答案对 \(998244353\) 取模。保证至少存在一个合法序列。
关键观察
1. \(p_i\) 的等价定义
因为只有 0 和 1 两种字符,所以对于位置 \(i\):
- 若 \(s_i = 0\),则 \(p_i\) 就是前 \(i\) 个字符中 \(1\) 的个数。
- 若 \(s_i = 1\),则 \(p_i\) 就是前 \(i\) 个字符中 \(0\) 的个数。
设当前前缀中 \(0\) 的个数为 \(a\),\(1\) 的个数为 \(b\)。那么:
- 选择 \(0\) 会产生 \(p = b\)
- 选择 \(1\) 会产生 \(p = a\)
2. 取反对称性
如果当前 \(a > b\),将整个序列取反(\(0\) 变 \(1\),\(1\) 变 \(0\)),所有 \(p_i\) 不变,因为条件 \([s_i \ne s_j]\) 在取反后依然成立。
因此可以强制规定 \(a \le b\),当 \(a > b\) 时交换 \(a\) 和 \(b\)。
3. 每一步的选择是被迫的
在 \(a \le b\) 的前提下:
-
当 \(a < b\) 时:
- 选 \(1\) 产生 \(p = a\)
- 选 \(0\) 产生 \(p = b\)
如果剩余多重集中还有 \(a\),那么这一步必须选 \(1\),消耗掉一个 \(a\)。
原因:若选 \(0\),则 \(a\) 变为 \(a+1\),之后所有产生的 \(p\) 值都至少为 \(a+1\),那个剩余的 \(a\) 就永远无法产生了。
如果剩余中已经没有 \(a\),则不能选 \(1\),只能选 \(0\),消耗一个 \(b\)。 -
当 \(a = b\) 时:
- 选 \(0\) 或选 \(1\) 都产生 \(p = a\)。
- 必须消耗一个 \(a\)。
- 两种选择会导向完全对称的后续构造,方案数乘以 \(2\)。
因此,整个构造过程中唯一有分支的地方就是 \(a = b\),且分支完全对称。
4. 为什么贪心不会“断”?
如果输入的 \(p'\) 确实来自某个合法序列,那么在那个合法序列中,每一步产生的 \(p\) 值正好就是算法每一步需要消耗的值。
因为算法的选择是唯一被迫的(除了 \(a = b\) 时的对称分支),任何合法序列在规范化后都必须遵循同样的选择。
所以,如果存在合法序列,算法消耗 \(p\) 值的顺序(在多重集意义下)与它完全一致,不会中途缺少需要的值。
如果中途发现需要的值不存在,说明根本不存在合法序列。题目保证至少有一个合法序列,所以实际不会断。
答案计算
设构造过程中出现 \(a = b\) 的次数为 \(k\)。
如果能够构造完所有位置,并恰好消耗完给定多重集中的全部元素,答案就是 \(2^k \bmod 998244353\);否则答案为 \(0\)。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll MOD = 998244353;
void solve() {
int n; cin >> n;
vector<int> cnt(n + 1, 0);
for(int i = 0; i < n; i ++) {
int x; cin >> x;
cnt[x] ++;
}
int a = 0, b = 0;
ll ans = 1;
for(int i = 1; i <= n; i ++) {
if(a == b) {
if(cnt[a] <= 0) {cout << "0\n"; return;}
b ++; cnt[a] --;
ans = ans * 2 % MOD;
}
else {
if(cnt[a] > 0) cnt[a] --, b ++;
else {
if(cnt[b] <= 0) {cout << "0\n"; return;}
cnt[b] --; a ++;
}
}
}
for(int i = 0; i <= n; i ++) {
if(cnt[i] != 0) {cout << "0\n"; return;}
}
cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int _ = 1; //cin >> _;
while(_--) solve();
return 0;
}

浙公网安备 33010602011771号