某年元宵节大礼包

题目描述

乐是做作业,给定正整数 N,M,要求计算Concatenate(1……N)mod M的值,其中Concatenate是指将1到N拼起来得到的数。如N=13,Concatenate=12345678910111213

输入格式

一行两个整数,N,M

输出格式

一个非负整数表示计算结果

样例输入

1
13 13

输出

1
4

数据范围

n1018,m109n \leq 10^{18},m\leq 10^9

分析

第一眼看到这题:暴力模拟

然后看到数据表示无语,1e18啊,这拼起来得多大

不管了先弄一个模拟:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
unsigned long long s;
int n, m;
int wei(int x) {
int s = 0;
while (x) {
s++;
x /= 10;
}
return s;
}
int main() {
freopen("le.in", "r", stdin);
freopen("le.out", "w", stdout);
cin >> n >> m;
for(int i = 1; i <= n; i++)
s = (s * pow(10, wei(i)) + i),
s = s % m;
cout << s;
return 0;
}

这个应该很好懂,就不解释了,30分

作为一个合格的OI选手,一定是要考虑正解的。

设ans[i]表示拼接到i位时的答案,我们很容易得到递推式:

ans[i]=(ans[i-1]*p+i)%mod;

其中p表示当前i的位数乘十,先不考虑mod,比如ans[4]=123*10+4;

于是这又是一个递推式问题,那么怎么办呢?暴力跑一遍?1e18的数据不TLE算我输

所以这个时候矩阵快速幂来了……

可以设一个矩阵A为

{ans[i] i 1} 反正我是习惯开一维数组。。。。

然后另一个矩阵K为

{p 0 0}

{1 1 0}

{1 1 1}

这样A*K就能得到{ans[i]*p+i i+1 1}即{ans[i+1] i+1 1}

是不是很巧妙,所以不难看出上述A中的1的作用——与ans[i],凑出ans[i+1]和i+1.

接着考虑乘多少次方的问题,我们从i=0开始,如果n=4,那么显然要乘四次;

如果n=14呢?从0到9,一共要乘9次,然后再从10到14乘5次

接着多次进行迭代不难发现,我们可以用变量data拷贝n

如果n>=p就乘9*p/10次,data减去乘的值

当n < p 时说明不够乘了,所以直接乘以data次

最后跑快速幂就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
ull n,mod,a[4],c[4][4];
void Mul(){
ull cp[4];
memset(cp,0,sizeof(cp));
for(int i=1;i<=3;i++){
for(int j=1;j<=3;j++){
cp[i]+=((a[j]%mod)*(c[j][i]%mod))%mod;
//乘之前一定要mod一下不然两个数乘积可能超出longlong
cp[i]%=mod;
}
}
memcpy(a,cp,sizeof(cp));
}
void Mulself(){
ull cp[4][4];
memset(cp,0,sizeof(cp));
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
for(int k=1;k<=3;k++){
cp[i][j]+=((c[i][k]%mod)*(c[k][j]%mod))%mod;
cp[i][j]%=mod;
}
memcpy(c,cp,sizeof(cp));
}
int main(){
scanf("%llu%llu",&n,&mod);
a[1]=0;a[2]=0;a[3]=1;
ull mx=n*10,data=n;
for(ull p=10LL;p<=mx;p*=10LL){
c[1][1]=p;c[1][2]=0;c[1][3]=0;
c[2][1]=1;c[2][2]=1;c[2][3]=0;
c[3][1]=1;c[3][2]=1;c[3][3]=1;
ull x;
if(n>=p){
x=9LL*p/10LL;
data-=x;
}
else x=data;
while(x){
if(x&1)Mul();
x>>=1;
Mulself();
}
}
printf("%llu\n",a[1]);
}

某年元宵节大礼包
https://suzipei.github.io/2020/03/07/hzoi0001/
作者
Su_Zipei
发布于
2020年3月7日
许可协议