题目大意

有一个 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;
}