題目連接:https://ac.nowcoder.com/acm/contest/882/Enode
題意:n×m的矩陣,0表示能夠走,1表示牆,不能經過。有q中操做,一種是改變座標(x,y)的狀態,一種是詢問從(1,x)到(n,y)有多少條路徑。(n,q<=5e4,m<=10)。ui
思路:dp、矩陣乘加線段樹維護。spa
dp[i][j]表示從第i行到(i,j)的路徑數。則dp[i][j]=sum(dp[i][k](k<=j)) + sum(dp[i][k](k>j))。code
好比上圖,dp[1][2]=dp[1][1]+dp[1][2]+dp[1][3]。blog
dp[1][6]=dp[1][5]+dp[1][6]。string
所以第x行到第y行的路徑數能夠用轉移矩陣表示,好比從第1行到第1行的轉移矩陣爲:it
其中第1行第3列的1表示從(1,1)到(1,3)有1條路經。io
用線段樹維護矩陣的乘積,修改成單點修改,複雜度爲O(m^3*logn),樹根維護的是M1*...*Mn,即第1行到第n行的路徑數,因此查詢複雜度爲O(1)。class
AC代碼:test
#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\n",tr[1].M.mat[x][y]); } return 0; }