[HNOI2011]数学作业

link

虽然很古老的时候(大概是2021年那个幸福但昏昏沉沉的寒假)已经自学过一阵矩阵乘法,但今天突然考到的时候还是想不起来,于是喜提暴力40分。

这道题的方程很好写。

\(f[i]=f[i-1]\times10^{\lfloor \log_{10}i\rfloor}+i\)

用矩阵来表示:

\[\begin{bmatrix}f_i\\i\\1\end{bmatrix}=\begin{bmatrix}10^k&1&1\\0&1&1\\0&0&1\end{bmatrix}\times\begin{bmatrix}f_{i-1}\\i-1\\1\end{bmatrix} \]

所以最后的那个结果矩阵可以看成是许许多多个转移矩阵相乘,然后取第一行第三个即可。但矩阵中有一个不确定元素?枚举即可。显然其最多有18种情况,把这18种情况加指数快速幂乘起来即可。

一定要注意顺序。矩阵乘法不满足交换律。

#include<bits/stdc++.h>
//#define zczc
#define int long long
using namespace std;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}
inline int min(int s1,int s2){
	return s1<s2?s1:s2;
}

int m,n;

struct node{
	int a[4][4];
}newone;
node operator *(node s1,node s2){
	node an=newone;
	for(int i=1;i<=3;i++){
		for(int j=1;j<=3;j++){
			for(int k=1;k<=3;k++){
				an.a[i][j]+=s1.a[i][k]*s2.a[k][j];
				an.a[i][j]%=n;
			}
		}
	}
	return an;
}
node qpow(node s1,int s2){
	if(s2==1)return s1;
	node an=qpow(s1,s2>>1);
	if(s2&1)return an*an*s1;
	else return an*an;
}

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);read(n);
	node ss=newone,an=newone;
	for(int i=1;i<=3;i++)an.a[i][i]=1;
	ss.a[1][2]=ss.a[1][3]=ss.a[2][2]=ss.a[2][3]=ss.a[3][3]=1;
	for(int now=10;;now*=10){
		int res=min(now-1,m)-now/10+1;
		if(res<=0)break;
		ss.a[1][1]=now%n;
		an=qpow(ss,res)*an;
	}
	printf("%lld\n",an.a[1][3]);
	
	return 0;
}

//948177742992423376 819143553
posted @ 2022-06-21 17:10  Feyn618  阅读(29)  评论(0编辑  收藏  举报