bzoj4417: [Shoi2013]超级跳马

本文介绍了一种使用矩阵快速幂解决特定类型问题的方法。通过构造特定矩阵并利用快速幂运算,可以高效求解涉及大量迭代的问题。文章提供了详细的代码实现,并解释了如何通过矩阵操作完成状态转移。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

自己看

题解

你就随便矩阵快速幂一下就可以了
首先这题不难想到一个nm的做法,就是保存奇数列和偶数列的答案
看看代码就懂了吧

#include<cstdio>
#include<cstring>
const int N=55;
int f[N][2];//哪一行  0:奇数列    1:偶数列 
int n,m;
int main()
{
    scanf("%d%d",&n,&m);
    memset(f,0,sizeof(f));
    f[1][0]=1;int now=0;
    for (int u=2;u<m;u++)
    {
        for (int i=1;i<=n;i++)
        {
            f[i][now^1]+=f[i-1][now]+f[i][now]+f[i+1][now];
        }
        now^=1;
    }
    printf("%d\n",f[n][now]+f[n-1][now]);
    return 0;
}

然后你就可以尝试构造一个矩阵,让他快速幂一下
方法有很多
着这里是吧相邻的两列放在一起快速幂
矩阵大小1*(2n)
1~n是奇数位的和 n+1~2*n是偶数位的和
然后大力推他由上一个状态怎么推过来。。

for (LL u=1;u<=n;u++)//先把前面的搞好
    {
        b.f[u][u]=1;
        b.f[u+n][u]=1;
        if (u>1) b.f[u+n][u-1]=1;
        if (u<n) b.f[u+n][u+1]=1;
    }
    for (LL u=n+1;u<=2*n;u++)
    {
        b.f[u][u]++;
        /*对应自己*/
        b.f[u-n][u]++;
        if (u+1<=2*n) b.f[u+1][u]++;
        b.f[u][u]++;
        if (u-1>n) b.f[u-1][u]++;
        /*对应+1*/
        if (u<2*n)
        {
            b.f[u-n+1][u]++;
            b.f[u][u]++;
            if (u+1<=2*n) b.f[u+1][u]++;
            if (u+2<=2*n) b.f[u+2][u]++;
        }
        /*对应-1*/
        if (u>n+1)
        {
            b.f[u][u]++;
            b.f[u-n-1][u]++;
            if (u-1>n) b.f[u-1][u]++;
            if (u-2>n) b.f[u-2][u]++;
        }
    }

小于n位的情况是比较直观的
但是大于n的位就要要论一下了,因为他需要转移的状态在上一个矩阵没有直观地出现。。于是要手完一下

然后就快速幂一下就好了。
要注意n=1的情况,否则会WA(但是对拍时抄了一个人的代码,他的没判,对拍挂了,交上去A了,我也不知道为什么)

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const LL N=55;
const LL MOD=30011;
struct qt
{
    LL x,y;//大小 
    LL f[N*2][N*2];
    friend qt operator * (qt a,qt b)
    {
        qt c;c.x=a.x;c.y=a.y;
        memset(c.f,0,sizeof(c.f));
        for (LL u=1;u<=a.x;u++)//行
            for (LL i=1;i<=a.y;i++)//列
                for (LL j=1;j<=a.y;j++) 
                    c.f[u][i]=(c.f[u][i]+(a.f[u][j]*b.f[j][i])%MOD)%MOD;
        return c;
    }
};//矩阵 
qt a;//初始矩阵 
qt b;//拿来乘的矩阵 
LL n,m; 
void prepare ()//构造初始矩阵 
{
    a.x=1;a.y=2*n;a.f[1][1]=1;a.f[1][n+1]=1;a.f[1][n+2]=1;
    b.x=2*n;b.y=2*n;
    for (LL u=1;u<=n;u++)//先把前面的搞好
    {
        b.f[u][u]=1;
        b.f[u+n][u]=1;
        if (u>1) b.f[u+n][u-1]=1;
        if (u<n) b.f[u+n][u+1]=1;
    }
    for (LL u=n+1;u<=2*n;u++)
    {
        b.f[u][u]++;
        /*对应自己*/
        b.f[u-n][u]++;
        if (u+1<=2*n) b.f[u+1][u]++;
        b.f[u][u]++;
        if (u-1>n) b.f[u-1][u]++;
        /*对应+1*/
        if (u<2*n)
        {
            b.f[u-n+1][u]++;
            b.f[u][u]++;
            if (u+1<=2*n) b.f[u+1][u]++;
            if (u+2<=2*n) b.f[u+2][u]++;
        }
        /*对应-1*/
        if (u>n+1)
        {
            b.f[u][u]++;
            b.f[u-n-1][u]++;
            if (u-1>n) b.f[u-1][u]++;
            if (u-2>n) b.f[u-2][u]++;
        }
    }
}
qt shen (qt x,LL y)
{
    /*printf("%d\n",y);
    if (y==1) return x;
    printf("YES\n");
    qt lalal=shen(x,y/2);
    lalal=lalal*lalal;
    printf("NO");
    if (y%2!=0) lalal=lalal*x;
    return lalal;*/
    qt ans;ans.x=x.x;ans.y=x.y;
    for(int i=1;i<=ans.x;i++) ans.f[i][i]=1;
    while(y)
    {
        if(y&1) ans=ans*x;
        x=x*x;y>>=1;
    }
    return ans;
}
void solve ()
{
    LL t=(m-2)/2;
    a=a*shen(b,t);
    if (m%2!=0)
    {
        if (n!=1)   printf("%lld\n",(a.f[1][n+n]+a.f[1][n+n-1])%MOD);
        else printf("%lld\n",a.f[1][2*n]);
    }
    else
    {
        if (n!=1) printf("%lld\n",(a.f[1][n]+a.f[1][n-1])%MOD);
        else printf("%lld\n",a.f[1][n]);
    }
}
int main()
{
    scanf("%lld%lld",&n,&m);
    prepare();
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值