2326: [HNOI2011]数学作业
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2552 Solved: 1507
[ Submit][ Status][ Discuss]
Description
小 C 数学成绩优异,于是老师给小 C 留了一道非常难的数学作业题:给定正整数 N 和 M
要求计算 Concatenate (1 .. N) Mod M 的值,其中 Concatenate (1 ..N)是将所有正整数 1, 2, …, N 顺序连接起来得到的数。
例如,N = 13, Concatenate (1 .. N)=12345678910111213.小C 想了大半天终于意识到这是一道不可能手算出来的题目,
于是他只好向你求助,希望你能编写一个程序帮他解决这个问题。
Input
只有一行且为用空格隔开的两个正整数N和M,
1≤N≤10^18且1≤M≤10^9.
Output
仅包含一个非负整数,表示 Concatenate (1 .. N) Mod M 的值。
Sample Input
13 13
Sample Output
4
HINT
Source
题解:好题!
想想看,如果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)相同划分为不同的区块进行转移优化。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=3;
struct node{
long long a[N][N];
void clr(){memset(a,0,sizeof(a));}
}ans,p[20];
long long n,mod,now,t;
node operator * (node x,node y)
{
node z; z.clr();
for (int i=0;i<3;++i)
for (int j=0;j<3;++j)
for (int k=0;k<3;++k)
z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j])%mod;
return z;
}
node Pow(node x,long long y)
{
node tmp=x;
for (;y;y>>=1,x=x*x) if (y&1) tmp=tmp*x;
return tmp;
}
long long calc(long long x,long long y)
{
long long tmp=1;
for (;y;y>>=1,x=x*x) if (y&1) tmp=tmp*x;
return tmp;
}
int main()
{
scanf("%lld%lld",&n,&mod);
ans.clr(); ans.a[2][0]=1;
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;
for (int i=1;i<=19;++i) p[i]=p[i-1],p[i].a[0][0]*=10,p[i].a[0][0]%=mod;
for (long long i=0,j=9,k=0;k<min(n,(long long)999999999999999999);k+=9*calc(10,i),++i)
ans=Pow(p[i+1],min(n-k,9*calc(10,i))-1)*ans;
if (n==1000000000000000000) ans=p[19]*ans;
printf("%lld\n",ans.a[0][0]);
return 0;
}