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

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

#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;
}
posted @ 2018-06-12 10:26  Maxwei_wzj  阅读(97)  评论(0编辑  收藏  举报