AtCoder Regular Contest 118 E Avoid Permutation
提供一种形式化的理解方法。
设 \(S\) 为任意一条从 \((0,0)\) 到 \((n+1,n+1)\) 的路径经过的点集,\(P\) 为 所有 合法障碍点集,\(Q\) 为 题目给定的 障碍点集。显然现在 \(S\) 和 \(P\) 都不确定,题目即求 \(\sum\limits_S \sum\limits_P [S \cap P = \varnothing]\)。
注意到可以容斥,即 \(ans = \sum\limits_S \sum\limits_P \sum\limits_{P' \subseteq S \cap P} (-1)^{|P'|}\)。
调换枚举顺序,得 \(ans = \sum\limits_{S} \sum\limits_{P' \subseteq S} (-1)^{|P'|} \sum\limits_{P' \subseteq P}1\)。
考虑若确定了 \(P'\),最后有 \(n - |P' \cup Q|\) 行和列是自由的,因此 \(P\) 的方案数为 \((n - |P' \cup Q|)!\),即 \(ans = \sum\limits_S \sum\limits_{P' \subseteq S} (-1)^{|P'|}(n - |P' \cup Q|)!\)。
此时考虑 dp,并把 \((-1)^{|P'|}\) 塞入转移中。设 \(f_{i,j,k,p=0/1,q=0/1}\) 表示当前走到 \((i,j)\),\(|P' \cup Q|=k\),第 \(i\) 行是否 \(\exists x,x \in P'\),第 \(j\) 列是否 \(\exists x,x \in P'\)。
转移枚举下一次走 \((i+1,j)\) 还是 \((i,j+1)\),以及这个点是否加入 \(P'\),即:
初值为 \(f_{0,0,|Q|,0,0} = 1\)。
最后 \(ans = \sum\limits_{i=0}^n (n-i)! \times f_{n+1,n+1,i,0,0}\)。
时间复杂度 \(O(n^3)\)。
code
// Problem: E - Avoid Permutations
// Contest: AtCoder - AtCoder Regular Contest 118
// URL: https://atcoder.jp/contests/arc118/tasks/arc118_e
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 210;
const ll mod = 998244353;
ll n, a[maxn], f[maxn][maxn][maxn][2][2], fac[maxn];
bool vis[maxn];
inline void upd(ll &x, ll y) {
x = (x + y) % mod;
}
void solve() {
scanf("%lld", &n);
fac[0] = 1;
int cnt = 0;
for (int i = 1; i <= n; ++i) {
scanf("%lld", &a[i]);
fac[i] = fac[i - 1] * i % mod;
if (a[i] > 0) {
++cnt;
vis[a[i]] = 1;
}
}
f[0][0][cnt][0][0] = 1;
for (int i = 0; i <= n + 1; ++i) {
for (int j = 0; j <= n + 1; ++j) {
for (int k = 0; k <= n; ++k) {
for (int p = 0; p < 2; ++p) {
for (int q = 0; q < 2; ++q) {
if (i <= n) {
upd(f[i + 1][j][k][0][q], f[i][j][k][p][q]);
if (!q && i < n && 1 <= j && j <= n && (a[i + 1] == j || (a[i + 1] == -1 && !vis[j]))) {
upd(f[i + 1][j][k + (a[i + 1] != j)][1][1], mod - f[i][j][k][p][q]);
}
}
if (j <= n) {
upd(f[i][j + 1][k][p][0], f[i][j][k][p][q]);
if (!p && j < n && 1 <= i && i <= n && (a[i] == j + 1 || (a[i] == -1 && !vis[j + 1]))) {
upd(f[i][j + 1][k + (a[i] != j + 1)][1][1], mod - f[i][j][k][p][q]);
}
}
}
}
}
}
}
ll ans = 0;
for (int i = 0; i <= n; ++i) {
upd(ans, fac[n - i] * f[n + 1][n + 1][i][0][0] % mod);
}
printf("%lld\n", ans);
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}