[HNOI2011]数学作业
虽然很古老的时候(大概是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
一如既往,万事胜意