1. 程式人生 > >洛谷 P3990 [SHOI2013]超級跳馬 解題報告

洛谷 P3990 [SHOI2013]超級跳馬 解題報告

P3990 [SHOI2013]超級跳馬

題目描述

現有一個\(n\)\(m\) 列的棋盤,一隻馬欲從棋盤的左上角跳到右下角。每一步它向右跳奇數列,且跳到本行或相鄰行。跳越期間,馬不能離開棋盤。

試求跳法種數\(\bmod 30011\)

輸入輸出格式

輸入格式:

僅有一行,包含兩個正整數\(n, m\),表示棋盤的規模。

輸出格式:

僅有一行,包含一個整數,即跳法種數\(\bmod 30011\)

說明

對於\(10\%\)的資料,\(1 ≤ n ≤ 10\)\(2 ≤ m ≤ 10\)

對於\(50\%\)的資料,\(1 ≤ n ≤ 10\)\(2 ≤ m ≤ 10^5\)

對於\(80\%\)的資料,\(1 ≤ n ≤ 10\)\(2 ≤ m ≤ 10^9\)

對於\(100\%\)的資料,\(1 ≤ n ≤ 50\)\(2 ≤ m ≤ 10^9\)


發現我的做法有點詭異...

思路:首先我們只考慮從左邊某一列的轉移,顯然可以構造這樣的一個轉移矩陣

\[\begin{bmatrix}1&1&0&0&\cdots&0&0&0\\1&1&1 &0&\cdots&0&0&0\\0&1&1 &1&\cdots&0&0&0\\ \vdots&\vdots&\vdots&\vdots&\ddots&\vdots&\vdots&\vdots\\0&0&0&0&\cdots&1&1&1\\0&0&0&0&\cdots&0&1&1\end{bmatrix}\]

然後設這個轉移矩陣為\(T\),設第\(i\)列的答案矩陣為\(A_i\)

\(A_n=T*(A_{n-1}+A_{n-3}+A_{n-5}+\dots)\)

\(A_{n-2}=T*(A_{n-3}+A_{n-5}+A_{n-7}+\dots)\)

那麼有\(A_n=T*A_{n-1}+A_{n-2}\),發現這是個遞推,於是再次構造矩陣加速轉移

\[\begin{bmatrix}T&I\\I&O\end{bmatrix}\begin{bmatrix}A_n\\A_{n-1}\end{bmatrix}=\begin{bmatrix}A_{n+1}\\A_{n}\end{bmatrix}\]

然後大力搞就行了,複雜度\(O(2^3n^3\log t)\)

注意一點\(A_3=TA_2\)不符合遞推式


Code:

#include <cstdio>
#include <cstring>
const int mod=30011;
int n,m;
struct matrix1
{
    int dx[52][52];
    matrix1(){memset(dx,0,sizeof(dx));}
    matrix1 friend operator *(matrix1 n1,matrix1 n2)
    {
        matrix1 n3;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                for(int k=1;k<=n;k++)
                    (n3.dx[i][j]+=n1.dx[i][k]*n2.dx[k][j])%=mod;
        return n3;
    }
    matrix1 friend operator +(matrix1 n1,matrix1 n2)
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                (n1.dx[i][j]+=n2.dx[i][j])%=mod;
        return n1;
    }
};
struct matrix2
{
    matrix1 dx[3][3];
    matrix2(){matrix1();}
    matrix2 friend operator *(matrix2 n1,matrix2 n2)
    {
        matrix2 n3;
        for(int i=1;i<=2;i++)
            for(int j=1;j<=2;j++)
                for(int i1=1;i1<=n;i1++)
                    for(int j1=1;j1<=n;j1++)
                        n3.dx[i][j].dx[i1][j1]=0;
        for(int i=1;i<=2;i++)
            for(int j=1;j<=2;j++)
                for(int k=1;k<=2;k++)
                    n3.dx[i][j]=n3.dx[i][j]+n1.dx[i][k]*n2.dx[k][j];
        return n3;
    }
}S,T,F;
int main()
{
    scanf("%d%d",&n,&m);
    S.dx[1][1].dx[1][1]=S.dx[1][1].dx[2][1]=1;
    for(int i=1;i<=n;i++)
    {
        T.dx[1][2].dx[i][i]=T.dx[2][1].dx[i][i]=1;
        T.dx[1][1].dx[i][i]=T.dx[1][1].dx[i][i-1]=T.dx[1][1].dx[i][i+1]=1;
    }
    if(m==2) return printf("%d\n",S.dx[1][1].dx[n][1]),0;
    m-=3,F=T;
    while(m)
    {
        if(m&1) F=F*T;
        T=T*T;
        m>>=1;
    }
    S=F*S;
    printf("%d\n",S.dx[1][1].dx[n][1]);
    return 0;
}

2018.12.19