2019牛客暑期多校训练营(第二场)-E MAZE

题目链接:https://ac.nowcoder.com/acm/contest/882/E

题意:n×m的矩阵,0表示可以走,1表示墙,不能通过。有q中操作,一种是改变坐标(x,y)的状态,一种是询问从(1,x)到(n,y)有多少条路径。(n,q<=5e4,m<=10)。

思路:dp、矩阵乘加线段树维护。

   dp[i][j]表示从第i行到(i,j)的路径数。则dp[i][j]=sum(dp[i][k](k<=j)) + sum(dp[i][k](k>j))。

     

   比如上图,dp[1][2]=dp[1][1]+dp[1][2]+dp[1][3]。

        dp[1][6]=dp[1][5]+dp[1][6]。

   因此第x行到第y行的路径数可以用转移矩阵表示,比如从第1行到第1行的转移矩阵为:

       

   其中第1行第3列的1表示从(1,1)到(1,3)有1条路经。

   用线段树维护矩阵的乘积,修改为单点修改,复杂度为O(m^3*logn),树根维护的是M1*...*Mn,即第1行到第n行的路径数,所以查询复杂度为O(1)。

   

AC代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

typedef long long LL;
const int maxn=50005;
const int MOD=1e9+7;

struct Mat{
    LL mat[12][12];
};

struct node{
    Mat M;
    int l,r;
}tr[maxn<<2];

int n,m,Q;
char s[12];
int a[maxn][12];

Mat Matmul(Mat& a,Mat& b){
    Mat c;
    for(int i=1;i<=m;++i)
        for(int j=1;j<=m;++j){
            c.mat[i][j]=0;
            for(int k=1;k<=m;++k){
                c.mat[i][j]+=a.mat[i][k]*b.mat[k][j]%MOD;
                c.mat[i][j]%=MOD;
            }
        }
    return c;
}

void Matupd(int v,int x){
    memset(tr[v].M.mat,0,sizeof(tr[v].M.mat));
    for(int i=1;i<=m;++i)
        if(!a[x][i]){
            tr[v].M.mat[i][i]=1;
            for(int j=i-1;j>=1&&!a[x][j];--j)
                tr[v].M.mat[j][i]=1;
            for(int j=i+1;j<=m&&!a[x][j];++j)
                tr[v].M.mat[j][i]=1;
        }
}

void pushup(int v){
    tr[v].M=Matmul(tr[v<<1].M,tr[v<<1|1].M);
}

void build(int v,int l,int r){
    tr[v].l=l,tr[v].r=r;
    if(l==r){
        Matupd(v,l);
        return;
    }
    int mid=(l+r)>>1;
    build(v<<1,l,mid);
    build(v<<1|1,mid+1,r);
    pushup(v);
}

void update(int v,int x){
    if(tr[v].l==tr[v].r){
        Matupd(v,x);
        return;
    }
    int mid=(tr[v].l+tr[v].r)>>1;
    if(x<=mid) update(v<<1,x);
    else update(v<<1|1,x);
    pushup(v);
}

int main(){
    scanf("%d%d%d",&n,&m,&Q);
    for(int i=1;i<=n;++i){
        scanf("%s",s);
        for(int j=1;j<=m;++j)
            a[i][j]=s[j-1]-'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;
            update(1,x);
        }
        else
            printf("%lld
",tr[1].M.mat[x][y]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/FrankChen831X/p/11249088.html