数学作业 递推+矩阵快速幂

数学作业

Solution:

设fi表示1~i构成的数除以M的余数,记x为i的位数。

不难写出递推式:

\[f_i=(f_{i-1}*10^x+i)\ mod\ M \]

但是线性的递推显然会TLE,所以考虑优化。

注意到x最大只能到18,所以可以把转移过程分成18段,这样每一段的x都相等

那么就可以利用矩阵快速幂加速递推了。

若状态矩阵为:

\[\left[\begin{array} {ccc} f_i&i+1&1 \end{array} \right] \]

转移矩阵应为:

\[{ \left[\begin{array} {ccc} 10^x & 0 & 0\\ 1 & 1 & 0\\ 0 & 1 & 1 \end{array} \right]} \]

Code:

#include<cmath>
#include<string>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define IL inline
#define int unsigned long long
#define DB double
using namespace std;

IL int gi() {
	char ch=getchar(); RG int x=0,w=0;
	while(ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
	while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
	return w?-x:x;
}

int n,mod;

struct Matrix {
	int MT[4][4];
	Matrix() {memset(MT,0,sizeof(MT));}
	IL void NewMT() {
		RG int i;
		for(i=0;i<=3;++i) MT[i][i]=1;
	}
	Matrix operator *(const Matrix &s) {
		RG int i,j,k;
		RG Matrix ans;
		for(i=1;i<=3;++i)
			for(j=1;j<=3;++j)
				for(k=1;k<=3;++k)
					(ans.MT[i][j]+=MT[i][k]*s.MT[k][j]%mod)%=mod;
		return ans;
	}
}f,g;

IL Matrix qpow(Matrix x,int p) {
	RG Matrix ans;
	ans.NewMT();
	for(;p;p>>=1,x=x*x)
		if(p&1) ans=ans*x;
	return ans;
}

signed main()
{
	RG int k,t,x,cnt=0;
	x=n=gi(),mod=gi();
	while(x) ++cnt,x/=10;
	f.MT[1][2]=f.MT[1][3]=1;
	g.MT[1][1]=g.MT[2][1]=g.MT[2][2]=g.MT[3][2]=g.MT[3][3]=1;
	for(k=1,t=9;k<cnt;++k) {
		g.MT[1][1]=g.MT[1][1]*10%mod;
		f=f*qpow(g,t),t=t*10;
	}
	g.MT[1][1]=g.MT[1][1]*10%mod;
	f=f*qpow(g,n-(int)pow(10,cnt-1)+1);
	printf("%llu\n",f.MT[1][1]);
	return 0;
}

posted @ 2019-04-04 15:41  薄荷凉了夏  阅读(165)  评论(0编辑  收藏  举报