§ Desc.
求出满足如下条件的 到 的排列数量 (记排列为 :
- , 有 .
, .
§ Sol.
很小, 从这里入手. 注意到 的状态可能有很多, 但 的状态比较少, 因此计算补集. 考虑容斥, 设 表示有 个数不合法, 剩余数随意放置的方案数, 为恰好 个不合法, 剩余数均合法的方案数. 有:
由二项式反演:
答案即为 . 考虑怎么求 . 设 表示前 个位置有 个不合法, 其余位置不纳入考虑1, 且第 个数的不合法区间当前的被占用情况为 的方案数. 转移分当前数是否加入不合法讨论即可, 具体可以看代码.
int main() {
ios::sync_with_stdio(0);
cin.tie(nullptr);
const int MOD = 998244353;
using mint = modint<MOD>;
int n, X; cin >> n >> X;
const int LIM = 1<<(2*X-1), U = LIM-1;
vector f(n+1, vector(n+1, vector<mint>(LIM)));
f[0][0][0] = 1;
for (int i=0;i<n;++i) {
for (int j=0;j<=i;++j) {
for (int s=0;s<LIM;++s) {
f[i+1][j][s>>1] += f[i][j][s];
for (int p=0;p<2*X-1;++p)
if (!((s>>1)&(1<<p)) && i+2-X+p >= 1 && i+2-X+p <= n)
f[i+1][j+1][(s>>1)|(1<<p)] += f[i][j][s];
}
}
}
vector<mint> g(n+1), facs(n+1);
facs[0] = 1;
for (int i=1;i<=n;++i)
facs[i] = facs[i-1]*i;
for (int i=0;i<=n;++i)
g[i] = accumulate(allu(f[n][i]), mint(0));
mint ans = 0;
for (int i=0;i<=n;++i)
ans += (i&1?MOD-1:1)*g[i]*facs[n-i];
cout << ans << "\n";
}
/ 来不及拒绝 /
/ 黑与白的纯洁 /
/ 视而不见 /
/ 假装不见 /
/ 凝望成永别 /
§ Footnotes
-
为什么其余位置不纳入考虑? 因为随意放置的方案数可以通过乘阶乘计算, 放入 DP 的计算里会很麻烦! ↩