P9493 「SFCOI-3」进行一个列的排
非常好的题,拜谢 irris 老师。
首先就是既然 $0\sim n-1$ 的 $\operatorname{mex}$ 都要出现,那么对于所有 $x$,排列中 $\le x$ 的数必然会构成一个连续的区间,也就是说这个序列以 $0$ 为分界点,左边单调下降,右边单调递增,可以证明或打表发现。
然后剩下的事情就很好办!设 $f_{i,j}$ 表示 $0\sim i$ 的数,填在 $j$ 开头的区间 $[j,j+i]$ 内的方案数。那么显然可以按当前最大值 $i$ 在最左端/最右端转移。最左端的转移即 $f_{i-1,j+1}$,此时要满足的条件是 $j+1$ 后面的位置要足够多,能组成一个长度为 $L_i$ 的区间,即 $n-j\ge L_i$;最右端的转移同理,$f_{i-1,j}$,条件是 $i+j-1\ge L_i$。
初始状态 $f_{0,j}$ 为 $[\max(j-1,n-j)\ge L_0]$,因为至少要有一个区间不包含 $0$,即 $\operatorname{mex}=0$。
滚动数组实现。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 5e3 + 10;
const int mod = 998244353;
int T, n;
int a[maxn];
int f[maxn];
signed main() {
cin >> T;
while (T--) {
memset(f, 0, sizeof(f));
bool fl = 1;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
if (a[i] < i) fl = 0;
}
if (!fl) {
cout << 0 << endl;
continue;
}
for (int j = 1; j <= n; j++) f[j] = max(j - 1, n - j) >= a[0];
for (int i = 1; i < n; i++) {
for (int j = 1; j <= n; j++) {
f[j] *= (i + j - 1 >= a[i]);
if (n - j >= a[i]) (f[j] += f[j + 1]) %= mod;
}
}
cout << f[1] << endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架