洛咕
题意:给定正整数(N)和(MOD),计算(Concatenate(1.. N)mod MOD) 的值,其中 $ Concatenate (1 .. N) $ 是将所有正整数(1,2,…,N)顺序连接起来得到的数.例如,(N = 13) , (Concatenate (1 .. N)=12345678910111213) .
分析:令(s_i)为前i个数得到的数,(len_i)表示i的位数,则不难写出递推式(s_i=s_{i-1}*10^{len_i}+i).发现如果(len_i)是固定的话,就是矩阵加速递推的基本做法了.再想一想,(len_i<=18),即最多只有18种可能,于是我们可以针对每一种可能都做一遍矩阵加速递推,只要考虑如何合并就好了.
先考虑转化为矩阵,假设我们已知(S(i-1)=[f(i-1),i,1]),要得到(S(i)=[f(i),i+1,1]),则可以得到转移矩阵(T=[egin{matrix} 10^{len_i} & 0 & 0 \1 & 1 & 0\0 & 1 & 1 end{matrix}]).
然后本题主要的难点在于如何合并?我们每次处理的是19,1099,100~999,我下面的代码中设置了一个for循环,让s从10开始每次乘10,还有一个now变量记录当前(上次)处理的是哪一段.所以我们当前要处理的段就是(s-1)-now.
#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline LL read(){
LL s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;
}
LL n,mod,now;
struct matrix{
LL a[3][3];
matrix operator *(matrix b){//重载运算符为矩阵乘法
matrix c;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
c.a[i][j]=0;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
for(int k=0;k<3;k++)
c.a[i][j]=(c.a[i][j]+1LL*a[i][k]*b.a[k][j])%mod;
return c;
}
}S,T;
int main(){
n=read();mod=read();
S.a[0][1]=S.a[0][2]=1;//S(0)=[0,1,1]
for(LL s=10;now<n;s*=10){
T.a[1][0]=T.a[1][1]=T.a[2][1]=T.a[2][2]=1;
T.a[0][1]=T.a[0][2]=T.a[1][2]=T.a[2][0]=0;
T.a[0][0]=s%mod;//10^len
LL b=min(s-1,n)-now;
//当前处理的段,这里s-1和n取min是要考虑到实际情况
while(b){//直接矩阵快速幂
if(b&1)S=S*T;
T=T*T;
b>>=1;
}
now=min(s-1,n);//now记录当前处理的段
}
printf("%lld
",S.a[0][0]);
return 0;
}