$$\Large\texttt{My Blog}$$


Description

题目链接:Luogu 3216

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

给定正整数 $n$ 和 $m$ ,要求计算 $\text{Concatenate}(1\dots n)\bmod m$ 的值,其中 $\text{Concatenate}(1\dots n)$ 是将所有正整数 $1,2,\dots,n$ 顺序连接起来得到的数。小 C 想了大半天终于意识到这是一道不可能手算出来的题目,于是他只好向你求助,希望你能编写一个程序帮他解决这个问题。

数据范围: $1\le n\le 10^{18}$ , $1\le m\le 10^9$


Solution

我们设前 $i$ 个数的答案为 $f_i$ ,那么可以列出如下转移方程: $$f_i=f_{i-1}+10^{\left\lfloor\log_{10}i\right\rfloor+1}+i$$ 发现第 $i$ 项之和第 $i-1$ 项有关,可以考虑矩阵快速幂优化递推。

转移矩阵为: $$\begin{bmatrix}f_n \\n \\1\end{bmatrix}=\begin{bmatrix}10^k & 1 & 1 \\0 & 1 & 1 \\0 & 0 & 1\end{bmatrix}\times\begin{bmatrix}f_{n-1} \\n-1 \\1\end{bmatrix}$$ 但是我们发现,指数 $k$ 的值为 $\left\lfloor\log_{10}i\right\rfloor+1$ (也就是 $len(i)$ )的值是会变化的,没法直接矩乘。由于 $len(i)$ 的值一共只有 $18$ 种左右,所以我们直接枚举 $len(i)$ 然后分块矩乘后合并。

注意:合并时,必须用转移矩阵的若干次方左乘答案矩阵!

时间复杂度: $O(3^3\log^2 n)$


Code

#include <cstdio>
#include <cstring>
#include <algorithm>

const int N=4;
int mod;

void upd(int &x,int y) {
    (x+=y)>=mod&&(x-=mod);
}
struct Matrix {
    int n,A[N][N];
    Matrix(int _n=0) {n=_n,memset(A,0,sizeof(A));}
    void operator ~ () {
        for(int i=0;i<n;++i) A[i][i]=1;
    }
    Matrix operator * (const Matrix &b) const {
        Matrix ret(n);
        for(int i=0;i<n;++i) for(int j=0;j<n;++j) for(int k=0;k<n;++k) {
            upd(ret.A[i][k],1LL*A[i][j]*b.A[j][k]%mod);
        }
        return ret;
    }
    Matrix operator ^ (const long long &b) const {
        Matrix ret(n),x=*this; ~ret;
        for(long long p=b;p;p>>=1,x=x*x) if(p&1) ret=ret*x;
        return ret;
    }
};

Matrix ans(3);
void calc(long long p,long long b) {
    Matrix x(3);
    x.A[0][0]=p%mod,x.A[0][1]=x.A[0][2]=x.A[1][1]=x.A[1][2]=x.A[2][2]=1;
    ans=(x^b)*ans;
}
int main() {
    long long n;
    scanf("%lld%d",&n,&mod);
    ~ans;
    long long r=10;
    while(r<=n) calc(r,r-(r/10)),r*=10;
    calc(r,n-r/10+1);
    printf("%d\n",ans.A[0][2]);
    return 0;
}