[luogu 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 的值。
输入输出样例
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:
#include<bits/stdc++.h> #define LL unsigned long long using namespace std; LL n,m,po[],lim[]; struct Mat { LL a[][]; Mat() {memset(a,,sizeof a);} }tran,ans; void pre() { tran.a[][]=,tran.a[][]=; tran.a[][]=,tran.a[][]=,tran.a[][]=; tran.a[][]=,tran.a[][]=,tran.a[][]=; po[]=,po[]=; ; i<=; i++) po[i]=po[i-]*; lim[]=; ; i<=; i++) lim[i]=lim[i-]*; lim[]=; ans.a[][]=,ans.a[][]=,ans.a[][]=; } Mat Mul(Mat u,Mat v) { Mat w; int i,j,k; ; i<; i++) ; j<; j++) ; k<; k++) (w.a[i][j]+=u.a[i][k]*v.a[k][j])%=m; return w; } Mat Qpow(Mat b,LL p) { ) return b; Mat t=Qpow(b,p/); t=Mul(t,t); ==?t:Mul(t,b); } int main() { cin>>n>>m,pre(); ; i<=; i++) ]<=n) { tran.a[][]=po[i+]%m; &&lim[i]-<=n) ans=Mul(Qpow(tran,lim[i]-lim[i-]),ans); ]+),ans); }else break; cout<<ans.a[][]<<'\n'; ; }