[矩阵乘法][DP]JZOJ 3294 超级跳马

Description

 

Input

仅有一行,包含两个正整数n, m,表示棋盘的规模。

Output

仅有一行,包含一个整数,即跳法种数mod 30011。
 

Sample Input

3 5

Sample Output

10
 

Data Constraint

对于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。

 

 分析

这题,你敢信我比赛的时候居然没想到前缀和优化DP……

f[i][j]表示第j行第i列的方案数,则有

$f[i][j]=\sum_{k=1}^{i/2}f[i-2*k+1][j\pm 1]$

设s1[i][j]表示距离当前列有奇数列(奇数猎?)的前缀和,s2差不多偶数猎

$s1[i][j]=s1[i-1][j\pm 1]+s2[i-1][j]$
$s2[i][j]=s1[i-1][j]$
可以把s2换成s1
$s1[i][j]=s1[i-1][j\pm 1]+s1[i-2][j]$
直接推会爆,所以可以矩阵乘法优化
设一个$1\times 2n$的矩阵A,前n个为s1[i],后n个为s1[i-1]
则要有矩阵T与A相乘得出B[s1[i+1],s1[i]]
然后根据上面的柿子,T为
?,单位矩阵
单位矩阵,空矩阵
?处可以较显然地看出为
1,1,0,0,0
1,1,1,0,0
0,1,1,1,0
0,0,1,1,1
0,0,0,1,1
(n=5)
然后快速幂即可
最后记住最后一个位置只能从两行转移,所以答案为s1[m][n]-s1[m-2][n]
 
#include <iostream>
#include <cstdio>
#include <memory.h>
using namespace std;
const int N=110;
const int P=3e4+11;
int n,m;
struct Rect {
    int r[N][N];
    Rect operator * (Rect &b) {
        Rect c;memset(c.r,0,sizeof c.r);
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                for (int k=1;k<=n;k++)
                    (c.r[i][j]+=r[i][k]*b.r[k][j]%P)%=P;
        return c;
    }
}T,A,B;

Rect Pow(Rect x,int y) {
    Rect ans;memset(ans.r,0,sizeof ans.r);
    for (int i=1;i<=n;i++) ans.r[i][i]=1;
    while (y) {
        if (y&1) ans=ans*x;
        x=x*x;
        y>>=1;
    }
    return ans;
}

int main() {
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) T.r[i][i]=T.r[i+n][i]=T.r[i][i+n]=1;
    for (int i=2;i<=n;i++) T.r[i-1][i]=T.r[i][i-1]=1;
    n<<=1;
    A=Pow(T,m-2);B=A*T;
    printf("%d",(B.r[1][n>>1]-A.r[1][n]+P)%P);
}
View Code

 

 

posted @ 2019-07-02 21:40  Vagari  阅读(232)  评论(0编辑  收藏  举报