题目描述
乐是做作业,给定正整数N,M,要求计算Concatenate(1……N)mod M的值,其中Concatenate是指将1到N拼起来得到的数。如N=13,Concatenate=12345678910111213
输入格式
一行两个整数,N,M
输出格式
一个非负整数表示计算结果
样例
输入 13 13
输出 4
数据范围
n<=1018,m<=109
分析
第一眼看到这题:暴力模拟
然后看到数据表示无语,1e18啊,这拼起来得多大
不管了先弄一个模拟:
#include#include #include using namespace std; unsigned long long s; int n, m; int wei(int x) { int s = 0; while (x) { s++; x /= 10; } return s; } int main() { freopen("le.in", "r", stdin); freopen("le.out", "w", stdout); cin >> n >> m; for(int i = 1; i <= n; i++) s = (s * pow(10, wei(i)) + i), s = s % m; cout << s; return 0; }
这个应该很好懂,就不解释了,30分
作为一个合格的OI选手,一定是要考虑正解的。
设ans[i]表示拼接到i位时的答案,我们很容易得到递推式:
ans[i]=(ans[i-1]*p+i)%mod;
其中p表示当前i的位数乘十,先不考虑mod,比如ans[4]=123*10+4;
于是这又是一个递推式问题,那么怎么办呢?暴力跑一遍?1e18的数据不TLE算我输
所以这个时候矩阵快速幂来了……
可以设一个矩阵A为
{ans[i] i 1} 反正我是习惯开一维数组。。。。
然后另一个矩阵K为
{p 0 0}
{1 1 0}
{1 1 1}
这样A*K就能得到{ans[i]*p+i i+1 1}即{ans[i+1] i+1 1}
是不是很巧妙,所以不难看出上述A中的1的作用——与ans[i],凑出ans[i+1]和i+1.
接着考虑乘多少次方的问题,我们从i=0开始,如果n=4,那么显然要乘四次;
如果n=14呢?从0到9,一共要乘9次,然后再从10到14乘5次
接着多次进行迭代不难发现,我们可以用变量data拷贝n
如果n>=p就乘9*p/10次,data减去乘的值
当n
最后跑快速幂就行了
#include#include #include using namespace std; typedef unsigned long long ull; ull n,mod,a[4],c[4][4]; void Mul(){ ull cp[4]; memset(cp,0,sizeof(cp)); for(int i=1;i<=3;i++){ for(int j=1;j<=3;j++){ cp[i]+=((a[j]%mod)*(c[j][i]%mod))%mod; //乘之前一定要mod一下不然两个数乘积可能超出longlong cp[i]%=mod; } } memcpy(a,cp,sizeof(cp)); } void Mulself(){ ull cp[4][4]; memset(cp,0,sizeof(cp)); for(int i=1;i<=3;i++) for(int j=1;j<=3;j++) for(int k=1;k<=3;k++){ cp[i][j]+=((c[i][k]%mod)*(c[k][j]%mod))%mod; cp[i][j]%=mod; } memcpy(c,cp,sizeof(cp)); } int main(){ scanf("%llu%llu",&n,&mod); a[1]=0;a[2]=0;a[3]=1; ull mx=n*10,data=n; for(ull p=10LL;p<=mx;p*=10LL){ c[1][1]=p;c[1][2]=0;c[1][3]=0; c[2][1]=1;c[2][2]=1;c[2][3]=0; c[3][1]=1;c[3][2]=1;c[3][3]=1; ull x; if(n>=p){ x=9LL*p/10LL; data-=x; } else x=data; while(x){ if(x&1)Mul(); x>>=1; Mulself(); } } printf("%llu\n",a[1]); }