4417: [Shoi2013]超级跳马|DP+矩阵快速幂

DP比较显然,用到矩阵乘法快速幂需要一点转换。
F1[i][j] 表示从起点走到第 (2i1) 列,第 j 行的方案数
F2[i][j]表示从起点走到第 (2i) 列,第 j 行的方案数
转移就是:

F1[i][j]=k<iF2[k][j1]+F2[k][j]+F2[k][j+1]

F2[i][j]=k<=iF1[k][j1]+F1[k][j]+F1[k][j+1]

显然需要分别把 F1 F2 数组转化为前缀和的形式,这样转移的复杂度就是 O(1)
转化为前缀和之后的转移:
F1[i][j]=F1[i1][j]+F2[i1][j1]+F2[i1][j]+F2[i1][j+1]

F2[i][j]=F2[i1][j]+F1[i][j1]+F1[i][j]+F1[i][j+1]

这样每一次转移都是相同的,然后就可以用矩阵快速幂加速转移。
PS: 到最后求出的是一个前缀和,所以还要做一次差才是答案!!!

#include<set>
#include<map>
#include<ctime>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define p 30011
using namespace std;
struct matrix {int a[110][110];}a,b,c;
int a1[110][110],a2[110][110],f[110];
int n,m,L;
matrix operator *(matrix a,matrix b)
{
    matrix c;
    for(int i=1;i<=L;i++)
        for(int j=1;j<=L;j++)
        {
            c.a[i][j]=0;
            for(int k=1;k<=L;k++)
                (c.a[i][j]+=a.a[i][k]*b.a[k][j])%=p;
        }
    return c;
}
int main()
{
    scanf("%d%d",&n,&m);L=2*n;
    if(m==2)
    {
        printf("%d\n",n>2?0:1);
        return 0;
    }   
    for(int i=1;i<=n;i++)
    {
        a2[i][i]=a2[i+n][i+n]=a1[i][i]=a1[i+n][i+n]=1;
        a1[i+n][i]=a2[i][i+n]=1;
        if(i>1)a1[i+n-1][i]=a2[i-1][i+n]=1;
        if(i<n)a1[i+n+1][i]=a2[i+1][i+n]=1;
    }
    for(int i=1;i<=L;i++)
        for(int j=1;j<=L;j++)
            for(int k=1;k<=L;k++)
                (a.a[i][j]+=a1[i][k]*a2[k][j])%=p;
    c=a;
    for(int i=1;i<=L;i++)b.a[i][i]=1;
    for(int y=(m-1)/2-1;y;y>>=1,a=a*a)if(y&1)b=b*a;
    c=b*c;
    int x=2*n;if(m&1)x-=n;
    int ans=(c.a[1][x]+c.a[n+1][x]+c.a[n+2][x]-(ans=b.a[1][x]+b.a[n+1][x]+b.a[n+2][x]))%p;
    printf("%d\n",(ans+p)%p);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值