1. 程式人生 > 其它 >【題解】ABC228G - Digits on Grid

【題解】ABC228G - Digits on Grid

給定一個 \(H\times W\) 的數字矩陣,一共走 \(2N\) 步,任選一個起點,奇數步可以移動到同行的一個點,偶數步移動到同列的一個點,將路徑上的數記錄下來得到一個長度為 \(2N\) 的序列(不包括起點),問有多少種可能的序列。

觀察到行數和列數很小,考慮可以狀態壓縮。

經過思考後我們可以設計一個很妙的狀態,\(f[i][S]\) 表示走了 \(i\) 步,結束時在集合 \(S\) 中的列/行,有多少種序列。

顯然有初始狀態 \(f[0][2^{H} - 1] = 1\),並且如果 \(S\) 不同,那麼兩個序列一定不同,可以分開轉移。

那麼對於 \(S\) 相同的序列,我們列舉後面接的數是什麼,然後根據這個數推出可以到達的行。

直接做是 \(\mathcal{O}(NHW(2^H+2^W))\),預處理後可以做到 \(\mathcal{O}((N+HW)(2^H+2^W))\)

#define N 11
int n, m, k, a[N][N], f[2][1 << N], u[1 << N][N], v[1 << N][N];
char s[N];
int main() {
	read(n), read(m), read(k);
	rp(i, n){
		scanf("%s", s + 1);
		rp(j, m)a[i][j] = s[j] - '0';
	}
	int p = (1 << n) - 1, q = (1 << m) - 1;
	rep(s, 1, p)rp(i, n)if((s >> (i - 1)) & 1)rp(j, m)u[s][a[i][j]] |= 1 << (j - 1);
	rep(s, 1, q)rp(j, m)if((s >> (j - 1)) & 1)rp(i, n)v[s][a[i][j]] |= 1 << (i - 1);
	f[0][p] = 1;
	rp(T, k){
		memset(f[1], 0, sizeof(f[1]));
		rp(s, p)
			rep(i, 0, 9)ad(f[1][u[s][i]], f[0][s]);
		memset(f[0], 0, sizeof(f[0]));
		rp(s, q)
			rep(i, 0, 9)ad(f[0][v[s][i]], f[1][s]);
	}
	int ans = 0;
	rep(j, 1, p)ad(ans, f[0][j]);
	printf("%d\n", ans);
	return 0;
}