AtCoder Regular Contest 118 E Avoid Permutation

洛谷传送门

AtCoder 传送门

提供一种形式化的理解方法。

\(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_{i,j,k,p,q} \to f_{i+1,j,k,0,q} \]

\[f_{i,j,k,p,q} \to (-1) \times f_{i+1,j,k+[a_{i+1} \ne j],1,1} \]

\[f_{i,j,k,p,q} \to f_{i,j+1,k,p,0} \]

\[f_{i,j,k,p,q} \to (-1) \times f_{i,j+1,k+[a_i \ne j+1],1,1} \]

初值为 \(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;
}

posted @ 2023-03-13 15:10  zltzlt  阅读(10)  评论(0编辑  收藏  举报