题目描述
小 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
一开始本来以为O(n)递推,看到数据范围矩阵乘法没得跑,我们构造一个矩阵,第一位放f[i],
第二位放[i],第三位放1,转移的时候f[i]∗10k+i+1(k是当前的位数),应该比较容易证明。
到进位的话k要处理一下。之后分析一下发现,我们可以把每个数拆掉之后乘比它位数少的...
那些矩阵的幂次再加上多出来的...md语言表达能力废了。举栗子好了。比如说n=105
那就是[10...]9∗[102...]90∗[103...]6∗[1,1,1]。
n=1003,就是[10...]9∗[102...]90∗[103...]900∗[104...]4∗[1,1,1]。
那个[10...]是特征矩阵的简写,表示[1][1]位置的数,其它不变。
特征矩阵长这样⎡⎣⎢10k11011001⎤⎦⎥
然后答案矩阵的话就是[111]
唔...表达不太清楚,还是自己推一下吧...蛮好推的辣...实现的话应该只要小小调一下...
嗯,应该不至于蠢得和我一样调几小时main函数...
#include <cstdio>
#include <algorithm>
#include <cstring>
#define Rep(i,s,t) for(int i=s;i<=t;i++)
using namespace std;
typedef long long LL;
LL lger=10,n,m,mimi=1;
namespace matrix_mul{
struct mat{
LL x[5][5];
mat(){memset(x,0,sizeof(x));}
};
mat mul(mat a,mat b){
mat tmp;
Rep(i,1,3) Rep(j,1,3) Rep(k,1,3)
tmp.x[i][j] = (tmp.x[i][j]+a.x[i][k]*b.x[k][j])%m;
return tmp;
}
mat power(mat a,LL n){
if(n == 0) return a;
mat ans = a,tmp = a;n--;
while(n){
if(n & 1) ans = mul(ans,tmp);
tmp = mul(tmp,tmp);
n >>= 1;
}
return ans;
}
}
int main(){
using namespace matrix_mul;
scanf("%lld%lld",&n,&m);
mat Ans,tmp,now;
Ans.x[1][1]=Ans.x[1][2]=Ans.x[1][3]=1;
tmp.x[2][1]=tmp.x[2][2]=tmp.x[3][1]=tmp.x[3][2]=tmp.x[3][3]=1;
tmp.x[1][1]=10;
if(n < 10){
Ans = mul(Ans,power(tmp,n-1));
printf("%lld\n",(Ans.x[1][1])%m);
return 0;
}
now = power(tmp,8);
while(lger <= n){
tmp.x[1][1] = lger*10%m;
if(lger*10 <= n)
now = mul(now,power(tmp,lger*9));
else now = mul(now,power(tmp,n-lger+1));
lger *= 10;
}
Ans = mul(Ans,now);
printf("%lld\n",(Ans.x[1][1])%m);
return 0;
}