[HNOI2011]数学作业

题目描述

小 C 数学成绩优异,于是老师给小 C 留了一道非常难的数学作业题:

给定正整数 \(n,m\),要求计算 \(\text{Concatenate}(n) \bmod \ m\) 的值,其中 \(\text{Concatenate}(n)\) 是将 \(1 \sim n\) 所有正整数 顺序连接起来得到的数。

例如,\(n = 13\) , \(\text{Concatenate}(n) = 12345678910111213\)。小C 想了大半天终于意识到这是一道不可能手算出来的题目,于是他只好向你求助,希望你能编写一个程序帮他解决这个问题。

输入格式

一行两个正整数 \(n,m\)

输出格式

输出一行一个整数表示答案。

样例 #1

样例输入 #1

13 13

样例输出 #1

4

提示

【数据范围】
对于 \(30\%\) 的数据,\(1\le n \le 10^6\)
对于 \(100\%\) 的数据,\(1\le n \le 10^{18}\)\(1\le m \le 10^9\)

思路

观察这个\(Concatenate\)数,不难得到递推式:

\[Concatenate[i]=Concatenate[i-1]\times10^{len(i)}+i \]

其中\(len(i)\)\(i\)的位数。

\(100\%\)数据是\(1\le n \le 10^{18}\),暴力推肯定不行,考虑矩阵加速。

设状态矩阵为\(\begin{bmatrix} F[i] & i & 1\end{bmatrix}\),它应该从\(\begin{bmatrix} F[i-1] & i-1 & 1\end{bmatrix}\)转移过来。因此可得转移矩阵为\(\begin{bmatrix} 10^{len(i)}&0&0\\1&1&0 \\1&1&1\end{bmatrix}\)。每次\(i\)的位数增加的时候更新\(len(i)\)的值即可。

代码

#include<cstdio>
#include<iostream>
#define int long long
using namespace std;
int n,mod,num,r,k,kk;
struct matrix
{
    int mat[4][4],n,m;
    matrix(int a,int b)
    {
        n=a,m=b;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                mat[i][j]=0;
    }
    void set_i()
    {
        for(int i=1;i<=n;i++)
                mat[i][i]=1;
    }
    matrix operator *(matrix b)
    {
        matrix ans(n,b.m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=b.m;j++)
                for(int k=1;k<=m;k++)
                    ans.mat[i][j]=(ans.mat[i][j]+(mat[i][k]*b.mat[k][j])%mod)%mod;
        return ans;
    }
};
matrix qpow(matrix a,int b)
{
    matrix ans(a.n,a.m);
    ans.set_i();
    while(b)
    {
        if(b&1)ans=ans*a;
        a=a*a;
        b>>=1;
    }
    return ans;
}
int numb(int x)
{
    int ret=0;
    while(x)
    {
        x/=10;
        ret++;
    }
    return ret;
}
signed main()
{
    scanf("%lld%lld",&n,&mod);
    num=numb(n);
    matrix f(1,3);
    f.mat[1][1]=f.mat[1][2]=f.mat[1][3]=1;
    matrix bb(3,3);
    bb.mat[1][1]=10;bb.mat[2][1]=bb.mat[2][2]=bb.mat[3][1]=bb.mat[3][2]=bb.mat[3][3]=1;
    matrix pro(3,3);
    pro.set_i();

    int mi=9,now=0;
    for(int i=1;i<num;i++)
    {
        now+=mi;
        if(i==1)pro=pro*qpow(bb,mi-1);
        else pro=pro*qpow(bb,mi);
        bb.mat[1][1]=(bb.mat[1][1]*10)%mod;
        mi=(mi<<1)+(mi<<3);
    }
    if(num==1)pro=pro*qpow(bb,n-now-1);
    else pro=pro*qpow(bb,n-now);
    f=f*pro;
    printf("%lld",f.mat[1][1]%mod);
    return 0;
}

 posted on 2022-09-23 19:40  hu_led  阅读(142)  评论(0编辑  收藏  举报