标签:变化 计算 pac output 数学 i++ 老师 hnoi2011 names
小 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 的值。
13 13
4
很显然,我们可以得到一个递推式:fn=10^len(n)*fn-1+n。然后看到范围那么大,而式子很简单,所以我们就将原式转为矩阵。
显然,根据原式,可以构造出如下矩阵:
{{fn} {{10^len(n),1,1} {{fn-1}
{n} = {0,1,1} * {n-1}
{1}} {0,0,1} {1}}
但是,我们发现,10^len(n)并不是一个常数。
怎么办?分段来求。1~9一段,10~99一段,100~999一段……10^k~n一段。
这样,就可以避免这一项会变化的情况了。
看起来非常的easy,是不是?写起来就不会这么觉得了。
特别是边界和细节,非常难以处理。调了整整2个多小时。。
code:
1 #include<bits/stdc++.h> 2 #define LL unsigned long long 3 using namespace std; 4 LL n,m,po[20],lim[20]; 5 struct Mat { 6 LL a[3][3]; Mat() {memset(a,0,sizeof a);} 7 }tran,ans; 8 void pre() { 9 tran.a[0][1]=1,tran.a[0][2]=1; 10 tran.a[1][0]=0,tran.a[1][1]=1,tran.a[1][2]=1; 11 tran.a[2][0]=0,tran.a[2][1]=0,tran.a[2][2]=1; 12 po[0]=0,po[1]=1; for (int i=2; i<=19; i++) po[i]=po[i-1]*10; 13 lim[0]=1; for (int i=1; i<=18; i++) lim[i]=lim[i-1]*10; lim[0]=2; 14 ans.a[0][0]=1,ans.a[1][0]=1,ans.a[2][0]=1; 15 } 16 Mat Mul(Mat u,Mat v) { 17 Mat w; int i,j,k; 18 for (i=0; i<3; i++) 19 for (j=0; j<3; j++) 20 for (k=0; k<3; k++) 21 (w.a[i][j]+=u.a[i][k]*v.a[k][j])%=m; 22 return w; 23 } 24 Mat Qpow(Mat b,LL p) { 25 if (p==1) return b; 26 Mat t=Qpow(b,p/2); t=Mul(t,t); 27 return p%2==0?t:Mul(t,b); 28 } 29 int main() { 30 cin>>n>>m,pre(); 31 for (int i=1; i<=18; i++) if (lim[i-1]<=n) { 32 tran.a[0][0]=po[i+1]%m; 33 if (i<18&&lim[i]-1<=n) ans=Mul(Qpow(tran,lim[i]-lim[i-1]),ans); 34 else if (lim[i]>n) ans=Mul(Qpow(tran,n-lim[i-1]+1),ans); 35 }else break; 36 cout<<ans.a[0][0]<<‘\n‘; 37 return 0; 38 }
标签:变化 计算 pac output 数学 i++ 老师 hnoi2011 names
原文地址:http://www.cnblogs.com/whc200305/p/7625815.html