题意简述
求 1234567891011121314151617181920 ⋯ n 1234567891011121314151617181920\cdots n 1234567891011121314151617181920⋯n(即 1 1 1到 n n n连在一起) m o d mod mod m m m的值。
数据
输入
第一行两个正整数 n , m n,m n,m, n < = 1 0 1 8 n<=10^18 n<=1018, m < = 1 0 9 m<=10^9 m<=109
输出
如题意简述。
样例
输入
13 13
输出
4
解释
12345678910111213 % 13 = 4
思路
我们会发现,设
f
(
n
)
f(n)
f(n)为
123456789101112
⋯
n
%
m
123456789101112\cdots n\%m
123456789101112⋯n%m,那么
f
(
n
)
=
f
(
n
−
1
)
×
1
0
b
+
n
f(n)=f(n-1)\times 10^b+n
f(n)=f(n−1)×10b+n,其中
b
b
b是
n
n
n的十进制位长度。我们发现它关系到几个数:
f
(
n
−
1
)
,
n
,
1
f(n-1),n,1
f(n−1),n,1(先写一个
1
1
1上来,万一过会缺常数呢。。。)。设计出这样的矩阵:
S
×
[
f
(
n
−
1
)
n
−
1
1
]
=
[
f
(
n
−
1
)
×
1
0
b
+
(
n
−
1
)
+
1
(
n
−
1
)
+
1
1
]
S \times\begin{bmatrix} f(n-1)\\ n-1\\ 1\\ \end{bmatrix}= \begin{bmatrix} f(n-1)\times10^b+(n-1)+1\\ (n-1)+1\\ 1 \end{bmatrix}
S×⎣⎡f(n−1)n−11⎦⎤=⎣⎡f(n−1)×10b+(n−1)+1(n−1)+11⎦⎤
先凑
S
S
S的第一行。根据矩阵的乘法法则,应该是有三个数,设为
a
,
b
,
c
a,b,c
a,b,c。那么应该满足:
a
f
(
n
−
1
)
+
b
(
n
−
1
)
+
c
=
f
(
n
−
1
)
×
1
0
b
+
(
n
−
1
)
+
1
af(n-1)+b(n-1)+c=f(n-1)\times10^b+(n-1)+1
af(n−1)+b(n−1)+c=f(n−1)×10b+(n−1)+1,(我就说会缺常数吧。。。所以才多放一个
1
1
1在这里)显然看出一个解
{
a
=
1
0
b
b
=
1
c
=
1
\begin{cases} a=10^b\\ b=1\\ c=1\\ \end{cases}
⎩⎪⎨⎪⎧a=10bb=1c=1
这就凑出了第一行
[
1
0
b
1
1
]
\begin{bmatrix} 10^b & 1 & 1 \end{bmatrix}
[10b11]
用类似的方法,珂以凑出转移矩阵
S
=
S=
S=
[
1
0
b
1
1
0
1
1
0
0
1
]
\begin{bmatrix} 10^b & 1 & 1\\ 0 & 1 & 1\\ 0 & 0 & 1\\ \end{bmatrix}
⎣⎡10b00110111⎦⎤
但是我们会发现这个
b
b
b特别讨厌:它会随着
n
n
n的增长而变化。那怎么办呢?打到矩阵里?反正我不会。。。(
b
b
b并不是乘上去的,而是作为指数的,所以没法弄)。但是这个
b
b
b增长的很慢,因为
n
<
=
1
0
1
8
n<=10^18
n<=1018,
b
b
b一共才
18
18
18个取值,那就分块求一下然后合并即可。也没多慢。
代码:
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
#define int long long
int n,m;
class Matrix//矩阵(方阵)类
{
#define N 5
private:
int a[N][N];
public:
int n;//矩阵大小
//初始化部分
Matrix()
{
memset(a,0,sizeof(a));
n=0;
}
Matrix(int _n)
{
memset(a,0,sizeof(a));
n=_n;
}
Matrix(int _n,int _x)
{_x%=m;
n=_n;
for(int i=0;i<N;++i)
{
for(int j=0;j<N;++j)
{
a[i][j]=_x;
}
}
}
//取值部分
int* operator[](int i)
{
return *(a+i);
}
//设值部分
void Set(int x)
{x%=m;
for(int i=0;i<N;++i)
{
for(int j=0;j<N;++j)
{
a[i][j]=x;
}
}
}
void Identity()
{
memset(a,0,sizeof(a));
for(int i=0;i<N;++i)
{
a[i][i]=1;
}
}
#undef N //5
};
Matrix operator*(Matrix x,Matrix y)//矩阵乘法
{
Matrix ans(x.n,0);
int n=ans.n;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
for(int k=1;k<=n;++k)
{
ans[i][j]+=x[i][k]*y[k][j];
ans[i][j]%=m;
}
}
}
return ans;
}
Matrix operator^(Matrix x,int p)//矩阵快速幂
{
Matrix ans(x.n,1);
ans.Identity();
while(p)
{
if (p&1) ans=ans*x;
x=x*x,p>>=1;
}
return ans;
}
Matrix ans;
void calc(int now,int p)//现在的底数是now(就是上面讲的10^b的值),要算p次方
{
Matrix TransMatrix(3,0);//TransMatrix就是S矩阵
TransMatrix[1][1]=now%m;
TransMatrix[1][2]=TransMatrix[1][3]=TransMatrix[2][2]=TransMatrix[2][3]=TransMatrix[3][3]=1;
ans=(TransMatrix^p)*ans;
}
void Solve()
{
ans.n=3;
ans.Identity();
int r=10;
while(r<=n) calc(r,r-(r/10)),r*=10;
//r=10^b
//有b-1个十进制位的自然数应该有10^b-10^(b-1)个,即r-r/10个
//分块即珂
calc(r,n-r/10+1);//最后乘上剩余部分
printf("%lld\n",ans[1][3]);
}
void Main()
{
if (0)
{
freopen("","r",stdin);
freopen("","w",stdout);
}
scanf("%lld%lld",&n,&m);
Solve();
}
#undef int //long long
};
main()
{
Flandle_Scarlet::Main();
return 0;
}