题目大意
有一个 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;
}
京公网安备 11010502036488号