题目大意:
题目链接:
洛谷:https://www.luogu.org/problemnew/show/P3216
Bzoj:https://www.lydsy.com/JudgeOnline/problem.php?id=2326
求 123... n ‾ % m \overline{123...n}\ \%\ m 123...n % m
思路:
这种矩阵乘法的题目一看
n
≤
1
0
18
n\leq 10^{18}
n≤1018的数据范围就明显地提示了算法。。。
思路还是很简单的。对于任意的
f
[
i
]
f[i]
f[i]表示
123...
i
‾
%
m
\overline{123...i}\ \%\ m
123...i % m,
f
[
i
+
1
]
f[i+1]
f[i+1]显然等于
(
f
[
i
]
×
c
n
t
+
i
+
1
)
%
m
(f[i]\times cnt+i+1)\ \%\ m
(f[i]×cnt+i+1) % m
其中
c
n
t
cnt
cnt表示
i
+
1
i+1
i+1的位数。
显然,对于任意不同位数的
i
i
i,需要分开来矩乘。

注意
l
o
n
g
l
o
n
g
×
l
o
n
g
l
o
n
g
long\ long\times long\ long
long long×long long可能会爆炸。
时间复杂度
O
(
l
o
g
n
)
O(log\ n)
O(log n)
代码:
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
ll n,k,MOD,f[4],a[4][4];
void mul(ll f[4],ll a[4][4])
{
ll c[4]={0,0,0,0};
for (int i=1;i<=3;i++)
for (int j=1;j<=3;j++)
c[i]=(c[i]+f[j]*a[i][j])%MOD;
memcpy(f,c,sizeof(c));
}
void mulself(ll a[4][4])
{
ll c[4][4];
memset(c,0,sizeof(c));
for (int i=1;i<=3;i++)
for (int j=1;j<=3;j++)
for (int k=1;k<=3;k++)
c[i][j]=(c[i][j]+a[i][k]*a[k][j])%MOD;
memcpy(a,c,sizeof(c));
}
int main()
{
scanf("%lld%lld",&n,&MOD);
f[1]=0,f[2]=0,f[3]=1;
for (ll m=1;m<=n;m*=10)
{
if (m>n/10) k=n-m+1; //这个位数要矩乘的次数
else k=m*9;
a[1][1]=1; a[1][2]=0; a[1][3]=1;
a[2][1]=1; a[2][2]=m*10%MOD; a[2][3]=1;
a[3][1]=0; a[3][2]=0; a[3][3]=1; //重置a数组
while (k)
{
if (k&1) mul(f,a);
mulself(a);
k>>=1;
}
}
printf("%lld",f[2]);
return 0;
}
本文介绍了一种解决大数取模问题的有效算法——矩阵快速幂。通过将问题转化为矩阵运算,利用快速幂的思想,可以高效求解123...n % m的问题,适用于n ≤ 10^18的数据范围。文章详细解析了算法思路,包括如何计算不同位数的数值,以及在长整型乘法中避免溢出的方法。
454





