数学作业 递推+矩阵快速幂

数学作业

Solution:

设fi表示1~i构成的数除以M的余数,记x为i的位数。

不难写出递推式:
\[ f_i=(f_{i-1}*10^x+i)\ mod\ M \]
但是线性的递推显然会TLE,所以考虑优化。

注意到x最大只能到18,所以可以把转移过程分成18段,这样每一段的x都相等

那么就可以利用矩阵快速幂加速递推了。

若状态矩阵为:
\[ \left[\begin{array} {ccc} f_i&i+1&1 \end{array} \right] \]
转移矩阵应为:
\[ { \left[\begin{array} {ccc} 10^x & 0 & 0\\ 1 & 1 & 0\\ 0 & 1 & 1 \end{array} \right]} \]
Code:

#include<cmath>
#include<string>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define IL inline
#define int unsigned long long
#define DB double
using namespace std;

IL int gi() {
    char ch=getchar(); RG int x=0,w=0;
    while(ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
    return w?-x:x;
}

int n,mod;

struct Matrix {
    int MT[4][4];
    Matrix() {memset(MT,0,sizeof(MT));}
    IL void NewMT() {
        RG int i;
        for(i=0;i<=3;++i) MT[i][i]=1;
    }
    Matrix operator *(const Matrix &s) {
        RG int i,j,k;
        RG Matrix ans;
        for(i=1;i<=3;++i)
            for(j=1;j<=3;++j)
                for(k=1;k<=3;++k)
                    (ans.MT[i][j]+=MT[i][k]*s.MT[k][j]%mod)%=mod;
        return ans;
    }
}f,g;

IL Matrix qpow(Matrix x,int p) {
    RG Matrix ans;
    ans.NewMT();
    for(;p;p>>=1,x=x*x)
        if(p&1) ans=ans*x;
    return ans;
}

signed main()
{
    RG int k,t,x,cnt=0;
    x=n=gi(),mod=gi();
    while(x) ++cnt,x/=10;
    f.MT[1][2]=f.MT[1][3]=1;
    g.MT[1][1]=g.MT[2][1]=g.MT[2][2]=g.MT[3][2]=g.MT[3][3]=1;
    for(k=1,t=9;k<cnt;++k) {
        g.MT[1][1]=g.MT[1][1]*10%mod;
        f=f*qpow(g,t),t=t*10;
    }
    g.MT[1][1]=g.MT[1][1]*10%mod;
    f=f*qpow(g,n-(int)pow(10,cnt-1)+1);
    printf("%llu\n",f.MT[1][1]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Bhllx/p/10655198.html
今日推荐