题目大意
有一个 n*m 的 01 矩阵,1 表示不可行,0 代表可行;
每次可以从 (i, j) 走到 (i, j – 1),(i, j + 1) 和 (i + 1, j),且不能回到已走过的格子;
有 q 个以下两种操作:
1、将某个格子的状态反转;
2、询问从 (1, x) 走到 (n, y) 的方案数。
1 <= n,q <= 5e4,1 <= m <= 10。
解题分析
先考虑不带修改的版本,设矩阵第 i 行第 j 个元素为 A[i][j];
dp[i][j] 表示从第 i – 1 行经过 (i – 1, j) 走到 (i, j) 的方案数,状态转移方程如下:
dp[i][j] = sum(dp[i – 1][k] for (k < j and A[i – 1][k] = A[i – 1][k + 1] = … = A[i – 1][j] = 0)) + sum(dp[i – 1][k] for k > j and A[i – 1][k] = A[i – 1][k – 1] = … = A[i – 1][j] = 0),if A[i][j] = 0。
简单来说,dp[i][j] 等于 A[i – 1, j] 向左和向右 A[i – 1][k] 都等于 0 的那些 dp[i – 1][k] 的和;
0 | 0 | 0 | 1 | 0 | 0 |
1 | 0 | 1 | 0 | 1 | 0 |
如当 n = 2,m = 6 时:
dp[2][2] = dp[1][1] + dp[1][2] + dp[1][3];
dp[2][6] = dp[1][5] + dp[1][6]。
因此第 i 行的 dp 值到第 i + 1 行的 dp 值的转移可用矩阵 Mi 实现;
如上图第一行的 dp 值到第二行 dp 值的转移矩阵 M1:
1 | 1 | 1 | 0 | 0 | 0 |
1 | 1 | 1 | 0 | 0 | 0 |
1 | 1 | 1 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 1 | 1 |
0 | 0 | 0 | 0 | 1 | 1 |
那么要求从 (1, x) 走到 (n, y) 的方案数,即令 dp[1][x] = 1,求 dp[n + 1][y];
为什么是 dp[n + 1][y] 而不是 dp[n][y],因为 dp[n][y] 表示的是从第 n – 1 行经过 (n – 1, y) 走到 (n, y) 的方案数,会漏掉那些从第 n 行走到 (n, y) 的方案数,而 dp[n + 1][y] 则表示从第 n 行经过 (n, y) 的所有方案数;
若令 ans = Π M1 ~ Mn,则取 ans[x][y]。
再考虑带修改的版本;
用线段树维护 M1 ~ Mn,反转 (x, y) 即重构 Mx,为单点修改,时间复杂度为 O(m^3logn);
树根维护的就是 Π M1 ~ Mn,可 O(1) 回答询问。
总时间复杂度 O(m^3logn)。
AC代码
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN = 50005; const int mod = 1e9 + 7; int n, m, q; char c[MAXN][12]; int a[MAXN][12]; struct Mat{ int m[12][12]; }st[MAXN << 2]; #define lson (p << 1) #define rson (p << 1 | 1) #define mid ((l + r) >> 1) int add(int a, int b){ return a + b >= mod ? a + b - mod : a + b; } int mul(ll a, int b){ return a * b >= mod ? a * b % mod : a * b; } Mat Mmul(Mat a, Mat b){ Mat c; for(int i = 1; i <= m; i ++) for(int j = 1; j <= m; j ++){ c.m[i][j] = 0; for(int k = 1; k <= m; k ++) c.m[i][j] = add(c.m[i][j], mul(a.m[i][k], b.m[k][j])); } return c; } void Mupd(int p, int x){ memset(st[p].m, 0, sizeof(st[p].m)); for(int i = 1; i <= m; i ++) if(!a[x][i]){ st[p].m[i][i] = 1; for(int j = i - 1; j >= 1 && !a[x][j]; j --) st[p].m[j][i] = 1; for(int j = i + 1; j <= m && !a[x][j]; j ++) st[p].m[j][i] = 1; } } void build(int p, int l, int r){ if(l == r){ Mupd(p, l); return; } build(lson, l, mid); build(rson, mid + 1, r); st[p] = Mmul(st[lson], st[rson]); } void upd(int p, int x, int y, int l, int r){ if(l == r){ Mupd(p, l); return; } if(x <= mid) upd(lson, x, y, l, mid); else upd(rson, x, y, mid + 1, r); st[p] = Mmul(st[lson], st[rson]); } int main(){ scanf("%d%d%d", &n, &m, &q); for(int i = 1; i <= n; i ++){ scanf("%s", c[i] + 1); for(int j = 1; j <= m; j ++) a[i][j] = c[i][j] - '0'; } build(1, 1, n); while(q --){ int op, x, y; scanf("%d%d%d", &op, &x, &y); if(op == 1){ a[x][y] ^= 1; upd(1, x, y, 1, n); } else printf("%d\n", st[1].m[x][y]); } return 0; }