【BZOJ2326】数学作业(HNOI2011)-递推+矩阵快速幂

测试地址:数学作业
做法:本题需要用到递推+矩阵快速幂。
f(i) f ( i ) Concatenate(i) C o n c a t e n a t e ( i ) m m 取模后的结果,很快能得到一个递推式:
f(i+1)=f(i)×10k+i+1
注意到所需的信息: f(i) f ( i ) i+1 i + 1 ,它们是可以同时递推的,所以我们就可以构造一个转移矩阵,使得能从向量 {f(i),i+1,1} { f ( i ) , i + 1 , 1 } 转移到 {f(i+1),i+2,1} { f ( i + 1 ) , i + 2 , 1 } ,应该很容易构造,实在不会请看代码。而 k k 的话最大也就19,因此我们只需对于 k=1,2,... k = 1 , 2 , . . . 的每段都求一遍矩阵快速幂即可,时间复杂度为 O(log2n) O ( log 2 ⁡ n )
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
ll n,mod;
struct matrix
{
    ll mat[3][3];
}M[110],S;

void mult(matrix &S,matrix A,matrix B)
{
    memset(S.mat,0,sizeof(S.mat));
    for(int i=0;i<=2;i++)
        for(int j=0;j<=2;j++)
            for(int k=0;k<=2;k++)
                S.mat[i][j]=(S.mat[i][j]+A.mat[i][k]*B.mat[k][j])%mod;
}

void power(matrix &S,ll p)
{
    int i=0;
    while(p)
    {
        if (p&1) mult(S,M[i],S);
        i++;p>>=1;
    }
}

int main()
{
    scanf("%llu%llu",&n,&mod);

    ll now=0,limit=10;

    memset(S.mat,0,sizeof(S.mat));
    S.mat[0][0]=S.mat[1][1]=S.mat[2][2]=1;

    while(now<n)
    {
        ll p;

        if (limit<=n) p=limit-1-now;
        else p=n-now;

        M[0].mat[0][0]=limit%mod,M[0].mat[0][1]=1,M[0].mat[0][2]=0;
        M[0].mat[1][0]=0,M[0].mat[1][1]=1,M[0].mat[1][2]=1;
        M[0].mat[2][0]=0,M[0].mat[2][1]=0,M[0].mat[2][2]=1;
        for(int i=1;i<=70;i++)
            mult(M[i],M[i-1],M[i-1]);

        power(S,p);

        now=limit-1;
        limit*=10;
    }
    printf("%llu",(S.mat[0][1]+S.mat[0][2])%mod);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值