洛谷P3216 [HNOI2011]数学作业

洛谷P3216 [HNOI2011]数学作业

题目描述

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

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

输入输出格式

输入格式:

 

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

 

输出格式:

 

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

 

输入输出样例

输入样例#1:
13 13
输出样例#1:
4


题解:想想看,如果n范围比较小时你会怎么做?
我们应该想到,比如说对于序列1234567,要取模的数是13,那么我们会先让1对13取模得到1,再让12对13取模得到12,再让123对13取模得到6,再让64对13取模得到12,再让125对13取模得到8,再让86对13取模得到8,最后让87对13取模得到9。这与1234567直接对13取模的值是相同的。其中的数学证明不再赘述。
于是我们可以得到递推式,对于序列f(n),f(n)=f(n-1)×10^len(n)+n(其中len(n)表示n这个数字有几位)。
由于n的范围极大,我们考虑用矩阵快速幂转移优化,构造矩阵如下:
f(n) 10^len(n) 1 1 f(n-1)
n = 0 1 1 × n-1
1 0 0 1 1
我们转移时,根据1~9时len(i)相同,10~99时len(i)相同,100~999时len(i)相同划分为不同的区块进行转移优化。
这个转移思路是从WHC大佬那里获得的,本蒟蒻还是太弱了。
接下来还是看代码吧。
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 const int N=3;
 6 struct node{
 7     long long a[N][N];
 8     void clr(){memset(a,0,sizeof(a));}
 9 }ans,p[20];
10 long long n,mod,now,t;
11 node operator * (node x,node y)
12 {
13     node z; z.clr();
14     for (int i=0;i<3;++i)
15     for (int j=0;j<3;++j)
16     for (int k=0;k<3;++k)
17     z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j])%mod;
18     return z;
19 }
20 node Pow(node x,long long y)
21 {
22     node tmp=x;
23     for (;y;y>>=1,x=x*x) if (y&1) tmp=tmp*x;
24     return tmp;
25 }
26 long long calc(long long x,long long y)
27 {
28     long long tmp=1;
29     for (;y;y>>=1,x=x*x) if (y&1) tmp=tmp*x;
30     return tmp;
31 }
32 int main()
33 {
34     scanf("%lld%lld",&n,&mod);
35     ans.clr(); ans.a[2][0]=1;
36     p[0].clr(); p[0].a[0][0]=p[0].a[0][1]=p[0].a[0][2]=p[0].a[1][1]=p[0].a[1][2]=p[0].a[2][2]=1;
37     for (int i=1;i<=19;++i) p[i]=p[i-1],p[i].a[0][0]*=10,p[i].a[0][0]%=mod;
38     for (long long i=0,j=9,k=0;k<min(n,(long long)999999999999999999);k+=9*calc(10,i),++i)
39     ans=Pow(p[i+1],min(n-k,9*calc(10,i))-1)*ans;
40     if (n==1000000000000000000) ans=p[19]*ans;
41     printf("%lld\n",ans.a[0][0]);
42     return 0;
43 }
View Code

 

 
posted @ 2017-11-07 20:46  氟铷氡氦  阅读(255)  评论(0编辑  收藏  举报