P3216 [HNOI2011]数学作业
题目
分析
首先容易写出方程是:\(dp[i]=dp[i-1]*10^{\lfloor lg(i) \rfloor+1}+i\) 。
然后我们发现这个\(*10^{\lfloor lg(i) \rfloor+1}\)并不好使用矩阵快速幂来优化。
但是这个值似乎很少,只在\([1,19]\)有取值。
于是考虑直接枚举这个值设为\(k\),那么方程就变成了 \(dp[i]=k\times dp[i-1]+i\) 。
这就是很容易转移的一个形式了,直接矩阵快速幂优化。
至于最后的答案就是就是把这些步骤全部拼起来即可,具体见代码。
代码
#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
x=0;char ch=getchar();bool f=false;
while(!isdigit(ch)){f|=ch=='-';ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
x=f?-x:x;
return ;
}
template <typename T>
inline void write(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
#define ll long long
ll n,m,MOD;
ll Pow[20];
struct Matrix{
ll a[3][3];
Matrix(){memset(a,0,sizeof(a));}
inline Matrix operator * (const Matrix &B)const{
Matrix C;
for(int i=0;i<3;i++){
for(int k=0;k<3;k++){
int r=a[i][k];
for(int j=0;j<3;j++) C.a[i][j]=(C.a[i][j]+r*B.a[k][j])%MOD;
}
}
return C;
}
};
struct Vector{
ll a[3];
Vector(){memset(a,0,sizeof(a));}
friend inline Vector operator * (const Vector &A,const Matrix &B){
Vector C;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++) C.a[i]=(C.a[i]+A.a[j]*B.a[j][i])%MOD;
}
return C;
}
};
inline Matrix QuickPow(Matrix x,ll y){
Matrix res;
for(int i=0;i<3;i++) res.a[i][i]=1;
while(y){
if(y&1) res=res*x;
x=x*x;
y>>=1;
}
return res;
}
signed main(){
read(n),read(MOD);
const int len=(int)floor(log(n)/log(10));
Pow[0]=1;
for(int i=1;i<=len+1;i++) Pow[i]=Pow[i-1]*10;
Vector now;
now.a[0]=0,now.a[1]=0,now.a[2]=1;
for(int i=0;i<len;i++){
Matrix trans;
trans.a[0][0]=Pow[i+1]%MOD;
trans.a[1][0]=trans.a[1][1]=trans.a[2][0]=trans.a[2][1]=trans.a[2][2]=1;
trans=QuickPow(trans,(9*Pow[i]));
now=now*trans;
}
Matrix trans;
trans.a[0][0]=Pow[len+1]%MOD;
trans.a[1][0]=trans.a[1][1]=trans.a[2][0]=trans.a[2][1]=trans.a[2][2]=1;
now=now*QuickPow(trans,n-Pow[len]+1);
write(now.a[0]);
return 0;
}
感受
这样“直接枚举不太好维护且值域较小的量”的技巧值得学习,在很多地方都有用到,可以当成一个小\(trick\)。