[HNOI 2011]数学作业

Description

小 C 数学成绩优异,于是老师给小 C 留了一道非常难的数学作业题:

给定正整数 N 和 M,要求计算 Concatenate (1 .. N) Mod M 的值,其中 Concatenate (1 ..N)是将所有正整数 1, 2, …, N 顺序连接起来得到的数。例如,N = 13, Concatenate (1 .. N)=12345678910111213.小C 想了大半天终于意识到这是一道不可能手算出来的题目,于是他只好向你求助,希望你能编写一个程序帮他解决这个问题。

Input

从文件input.txt中读入数据,输入文件只有一行且为用空格隔开的两个正整数N和M,其中30%的数据满足1≤N≤1000000;100%的数据满足1≤N≤1018且1≤M≤109.

Output

输出文件 output.txt 仅包含一个非负整数,表示 Concatenate (1 .. N) Mod M 的值。

Sample Input

13 13

Sample Output

4

题解

首先写出递推关系式:令 $s_i$ 为前 i 个数连接得到的数,$c(i)$ 表示 $i$ 的位数,有

$s_i=10^{c(i)}×s_{i−1}+i$

$c(i)$ 随 $i$ 变化,无法在转移矩阵 $T$ 中表示。如果 $c(i)$ 固定,就可以矩阵优化了!

考虑位数随着 $i$ 的变化最多只会变化 $18$ 次,因此可以按位数分段进行矩阵快速幂。
枚举位数,那么有

这里在状态矩阵中加了一个永远为 $1$ 的值,用于辅助 $i$ 每次加一。

 1 #include<map>
 2 #include<cmath>
 3 #include<ctime>
 4 #include<queue>
 5 #include<stack>
 6 #include<cstdio>
 7 #include<string>
 8 #include<vector>
 9 #include<cstring>
10 #include<cstdlib>
11 #include<iostream>
12 #include<algorithm>
13 #define LL long long
14 #define RE register
15 #define IL inline
16  using namespace std;
17 
18 LL n,m;
19 struct mat
20 {
21     LL a[3][3];
22     mat(){for (int i=0;i<3;i++)for (int j=0;j<3;j++) a[i][j]=0;}
23     mat operator * (const mat&b)
24     {
25         mat ans;
26         for (int i=0;i<3;i++)
27             for (int j=0;j<3;j++)
28                 for (int k=0;k<3;k++)
29                     ans.a[i][j]=(ans.a[i][j]+(a[i][k]*b.a[k][j])%m)%m;
30         return ans;
31     }
32 }S;
33 
34 int main()
35 {
36     S.a[0][1]=S.a[0][2]=1;
37     scanf("%lld%lld",&n,&m);
38     LL now=0;
39     for (LL s=10;now<n;s*=10)
40     {
41         mat T;
42         T.a[1][0]=T.a[1][1]=T.a[2][1]=T.a[2][2]=1;
43         T.a[0][0]=s%m;
44         LL nd=min(s-1,n)-now;
45         while (nd)
46         {
47             if (nd&1) S=S*T;
48             T=T*T;
49             nd>>=1;
50         }
51         now=min(s-1,n);
52     }
53     printf("%lld\n",S.a[0][0]);
54     return 0;
55 }

 

posted @ 2017-07-31 21:34  NaVi_Awson  阅读(434)  评论(1编辑  收藏  举报