[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 }
博主蒟蒻,随意转载。但必须附上原文链接:http://www.cnblogs.com/NaVi-Awson/,否则你会终生找不到妹子!!!