[SHOI2013] 超级跳马

题意:

现有一个n行m列的棋盘,一只马欲从棋盘的左上角跳到右下角。

每一步它向右跳奇数列,且跳到本行或相邻行,但不能离开棋盘。

求跳的方案数,对30011取模。

$nleq 50,mleq 10^{9}$。

题解:

挺水的一道题。设$dp_{i,j}$为走到$(i,j)$的方案数,那么$dp_{i,j}=sum limits_{k\%2 eq j\%2}^{k<j}{dp_{i-1,k}+dp_{i,k}+dp_{i+1,k}}$。

令$F_{i,j}=sum limits_{k\%2=0}^{kleq j}{dp_{i,k}},G_{i,j}=sum limits_{k\%2=1}^{kleq j}{dp_{i,k}}$,那么

  • 若$j\%2=1$,则$F_{i,j}=F_{i,j-1},G_{i,j}=G_{i,j-1}+F_{i-1,j-1}+F_{i,j-1}+F_{i+1,j-1}$。
  • 若$j\%2=0$,则$F_{i,j}=F_{i,j-1}+G_{i-1,j-1}+G_{i,j-1}+G_{i+1,j-1},G_{i,j}=G_{i,j-1}$。

于是直接用$2n imes 2n$的矩阵转移F和G,最后用两个前缀和相减即可得到答案。

复杂度$O(n^{3}log{m})$。注意矩阵乘法不满足交换律,必须按顺序乘。

套路:

  • 矩阵乘法优化dp:i状态能转移到j状态$ ightarrow A_{i,j}=1$。
  • 形如$dp_{i,j}=sum dp_{k,j-1}$的转移$ ightarrow$矩阵乘法优化。
  • 矩阵乘法:不满足交换律,必须按顺序乘。

代码:

#include<bits/stdc++.h>
#define maxn 205
#define maxm 500005
#define inf 0x7fffffff
#define ll long long
#define mod 30011
#define rint register ll
#define debug(x) cerr<<#x<<": "<<x<<endl
#define fgx cerr<<"--------------"<<endl
#define dgx cerr<<"=============="<<endl

using namespace std;
struct Matrix{
    ll x,y,M[maxn][maxn];
    Matrix operator*(const Matrix b)const{
        Matrix res; res.x=x,res.y=b.y;
        for(ll i=1;i<=x;i++)
            for(ll j=1;j<=b.y;j++){
                res.M[i][j]=0;
                for(ll k=1;k<=y;k++)
                    res.M[i][j]=(res.M[i][j]+M[i][k]*b.M[k][j]%mod)%mod;
            }
        return res;
    }
}A,B,C;

inline ll read(){
    ll x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

inline Matrix pw(Matrix a,ll b){
    Matrix res; res.x=res.y=0;
    while(b){
        if(b&1) res=(!res.x)?a:res*a;
        a=a*a,b>>=1;
    } 
    return res;
}

inline Matrix solve(int m){
    if(m&1) return A*pw(C*B,m/2)*C;
    else return A*pw(C*B,m/2);
}

/*inline void print(Matrix a){
    for(int i=1;i<=a.x;i++){
        for(int j=1;j<=a.y;j++)
            cout<<a.M[i][j]<<" ";
        cout<<endl;    
    }
    fgx;
}*/

int main(){
    ll n=read(),m=read();
    A.x=1,A.y=2*n,B.x=B.y=C.x=C.y=2*n,A.M[1][1]=1;
    for(ll i=1;i<=n;i++){
        B.M[i][i]=1,B.M[i+n][i+n]=1,B.M[i+n][i]=1;
        if(i>1) B.M[i+n-1][i]=1; 
        if(i<n) B.M[i+n+1][i]=1;
    }
    for(ll i=1;i<=n;i++){
        C.M[i][i]=1,C.M[i+n][i+n]=1,C.M[i][i+n]=1;
        if(i>1) C.M[i-1][i+n]=1; 
        if(i<n) C.M[i+1][i+n]=1;
    }
    //print(A),print(B),print(C);
    Matrix t1=solve(m-1),t2=solve(m-2);
    //print(t1),print(t2);
    if(m%2) printf("%lld
",(t1.M[1][n]-t2.M[1][n]+mod)%mod);
    else printf("%lld
",(t1.M[1][2*n]-t2.M[1][2*n]+mod)%mod);
    return 0;
}
超级跳马
原文地址:https://www.cnblogs.com/YSFAC/p/13237959.html