题目描述
小 C 数学成绩优异,于是老师给小 C 留了一道非常难的数学作业题:给定正整数 N 和 M
要求计算
Concatenate(1..N)
C
o
n
c
a
t
e
n
a
t
e
(
1
.
.
N
)
Mod
M
o
d
M
M
的值,其中 Concatenate (1 ..N)是将所有正整数
1,2,…,N
1
,
2
,
…
,
N
顺序连接起来得到的数。
例如,
N=13
N
=
13
,
Concatenate(1..N)
C
o
n
c
a
t
e
n
a
t
e
(
1
.
.
N
)
=
12345678910111213
12345678910111213
.小C 想了大半天终于意识到这是一道不可能手算出来的题目,
于是他只好向你求助,希望你能编写一个程序帮他解决这个问题。
输入
只有一行且为用空格隔开的两个正整数N和M,
1≤N≤1018
1
≤
N
≤
10
18
且
1≤M≤109
1
≤
M
≤
10
9
.
输出
仅包含一个非负整数,表示 Concatenate (1 .. N) Mod M 的值。
Solution
看到数据就知道是矩阵乘法
递推式也比较简单
fi f i = = ∗ ∗ +i + i
可是i的位数,也就是
⌊log10i⌋
⌊
l
o
g
10
i
⌋
并不能线性变换出来怎么办
直接分组,因为最多只有18种位数,那就做18次矩阵快速幂,每一次
⌊log10i⌋
⌊
l
o
g
10
i
⌋
是固定的,就比较好求了
#include<cstring>
#include<algorithm>
using namespace std;
typedef int _int;
#define int long long
int m;
struct MAT{
int a[4][4];
void init(int flg){
memset(a,0,sizeof(a));
if(flg) for(int i=1;i<=3;++i) a[i][i]=1;
}
MAT operator*(const MAT t){
MAT c;
c.init(0);
for(int k=1;k<=3;++k)
for(int i=1;i<=3;++i)
for(int j=1;j<=3;++j)
(c.a[i][j]+=a[i][k]*t.a[k][j])%=m;
return c;
}
MAT operator^(const int B){
int b=B;
MAT X=*this,t;
t.init(1);
while(b){
if(b&1) t=t*X;
b>>=1;
X=X*X;
}
return t;
}
};
MAT cal(int x,int k){
MAT tmp;
tmp.init(1);
tmp.a[1][1]=k,tmp.a[2][1]=tmp.a[3][2]=1;
return tmp^x;
}
int p[20];
_int main(){
int n;
scanf("%lld%lld",&n,&m);
MAT ans;
ans.init(1),ans.a[1][1]=0,ans.a[1][3]=1;
p[0]=1;
ans=ans*cal(1,1);
for(int i=1;i<=18;++i){
p[i]=p[i-1]*10;
int L=p[i-1],R=min(p[i]-1,n);
ans=ans*cal(R-L+1,p[i]%m);
//之前把R-L+1取了模,所以我还太年轻,动不动就乱膜
if(n==R) break;
}
printf("%lld\n",ans.a[1][1]);
return 0;
}