线性代数
线性代数
P1939 矩阵加速(数列)
看到数据范围和题目格式就知道这道题要用矩阵来加速,接下来便是要考虑单位矩阵和初始矩阵的问题。对于初始矩阵,它一般是一个 \(1\times k\) 的矩阵,因为有转移式有三个元素,于是初始矩阵便是
\[\begin{bmatrix}a_x\\a_{x-1}\\a_{x-2}\end{bmatrix}
\]
这样来看我们要构造的单位矩阵大小便为 \(3 \times 3\) 我们设它为
\[\begin{bmatrix}k_1&k_2&k_3\\k_4&k_5&k_6\\k_7&k_8&k_9\end{bmatrix}
\]
那么就有
\[\begin{bmatrix}a_x\\a_{x-1}\\a_{x-2}\end{bmatrix}=
\begin{bmatrix}k_1&k_2&k_3\\k_4&k_5&k_6\\k_7&k_8&k_9\end{bmatrix}\times
\begin{bmatrix}a_{x-1}\\a_{x-2}\\a_{x-3}\end{bmatrix}
\]
\[a_{x} = a_{x-1} \times k_1 + a_{x-2} \times k_2 + a_{x-3} \times k_3 \\
a_{x-1} = a_{x-1} \times k_4 + a_{x-2} \times k_5 + a_{x-3} \times k_6 \\
a_{x-2} = a_{x-1} \times k_7 + a_{x-2} \times k_8 + a_{x-3} \times k_9 \\
\]
所以解出来单位矩阵为
\[\begin{bmatrix}1&0&1\\1&0&0\\0&1&0\end{bmatrix}
\]
最后矩阵快速幂算出结果即可。
\[\begin{bmatrix}a_x&a_{x-1}&a_{x-2}\end{bmatrix}= \begin{bmatrix}a_{x-1}&a_{x-2}&a_{x-3}\end{bmatrix}
\times
\begin{bmatrix}
1 & 1 & 0\\
1 & 0 & 0\\
0 & 1 & 0
\end{bmatrix}
\]
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll mo[8][8],num[8],n;
void mul1(ll a[8][8],ll b[8][8]){//单位矩阵相乘
ll t[8][8]={};
for(int i=1;i<=3;i++){
for(int j=1;j<=3;j++){
for(int k=1;k<=3;k++){
t[i][j]=(t[i][j]+a[i][k]*b[k][j])%mod;
}
}
}
memcpy(a,t,sizeof(t));
}
void mul2(ll a[],ll b[8][8]){
ll t[8]={};
for(int i=1;i<=3;i++){
for(int j=1;j<=3;j++){
t[i]=(t[i]+a[j]*b[i][j])%mod;
}
}
memcpy(a,t,sizeof(t));
}
void ksm(ll a[],int n){
while(n){
if(n&1){
mul2(a,mo);
}
mul1(mo,mo);
n>>=1;
}
}
int main(){
int T;
cin>>T;//多测
while(T--){
cin>>n;
num[1]=1,num[2]=1,num[3]=1;
mo[1][1]=1,mo[1][2]=0,mo[1][3]=1;
mo[2][1]=1,mo[2][2]=0,mo[2][3]=0;
mo[3][1]=0,mo[3][2]=1,mo[3][3]=0;
if(n<=3){//特判
cout<<num[n]<<"\n";
continue;
}
ksm(num,n-3);
cout<<num[1]<<"\n";
}
return 0;
}
[P3216 HNOI2011] 数学作业
根据题意,我们设$\text{Concatenate}(n) $ 为 $ f[n] $ 便可以得到这样的转移关系 $ \textit{f}[i]=\left(\textit{f}[i-1]\times10^{1+\lg i}\right)+i $
根据这个式子可以我们得到初始矩阵
\[\begin{bmatrix}
\textit{f}[i] \\ i+1 \\ 1
\end{bmatrix}
\]
再由初始矩阵我们可以计算出单位矩阵
\[\begin{bmatrix}f[i]\\i+1\\1\end{bmatrix} =
\begin{bmatrix}k_1&k_2&k_3\\k_4&k_5&k_6\\k_7&k_8&k_9\end{bmatrix} \times
\begin{bmatrix}f[i-1]\\i\\1\end{bmatrix}
\]
\[f[i] = f[i-1] \times k_1 + i \times k_2 + 1 \times k_3 \\
i+1 = f[i-1] \times k_4 + i \times k_5 + 1 \times k_6 \\
1 = f[i-1] \times k_7 + i \times k_8 + 1 \times k_9 \\
\]
解得单位矩阵为
\[\begin{bmatrix}
10^k& 1 & 0 \\
0 & 1 & 1 \\
0 & 0 & 1
\end{bmatrix} \\
其中 k=\lg i+1
\]
我们发现 \(k\) 会随着 \(i\) 改变,这在矩阵快速幂中是不允许的,因此不能一次矩阵快速幂解决。我们需要根据每一次的 \(i\) 构造转移矩阵,每次快速幂的幂次这一层转移的次数,即在先计算每层快速幂再乘如单位矩阵,每层是 \([10^k,10^{k+1}-10^k]\)。
\[\begin{bmatrix}f[i]&i+1&1\end{bmatrix} = \begin{bmatrix}f[i-1]&i&1\end{bmatrix} \times
\begin{bmatrix}
10^{\lg i+1}& 0 & 0 \\
1 & 1 & 0 \\
0 & 1 & 1
\end{bmatrix} \\
\]
#include<bits/stdc++.h>//60分代码
using namespace std;
typedef unsigned long long ll;//10的19次方会爆long long
ll mo[4][4],num[4],n,m,ans;
void mul1(ll a[],ll b[4][4]){
ll t[4]={0};
for(int i=1;i<=3;i++){
for(int j=1;j<=3;j++){
t[i]=(t[i]+a[j]*b[j][i]%m)%m;
}
}
memcpy(a,t,sizeof(t));
}
void mul2(ll a[4][4],ll b[4][4]){
ll t[4][4]={};
for(int i=1;i<=3;i++){
for(int j=1;j<=3;j++){
for(int k=1;k<=3;k++){
t[i][j]=(t[i][j]+a[i][k]*b[k][j]%m)%m;
}
}
}
memcpy(a,t,sizeof(t));
}
void ksm(ll n){//这里应该先将单位矩阵快速幂再乘初始矩阵
ll t[4][4]={};
for(int i=1;i<=3;i++){
t[i][i]=1;//3x3的“单位矩阵”(此单位矩阵非彼单位矩阵)
}
while(n){
if(n&1){
mul2(t,mo);
}
mul2(mo,mo);
n>>=1;
}
memcpy(mo,t,sizeof(t));
}
ll bpow(ll x,ll y){//pow函数在10的15次方会爆
ll res=1;
for(int i=1;i<=y;i++){
res=res*x;
}
return res;
}
int main(){
cin>>n>>m;
int k=1;
while(1){
ll l,r;
if(k==1){
l=1;
}else{
l=bpow(10,k-1);
}
if(l>n){
break;
}
r=min(bpow(10,k)-1,n);
ll len=r-l+1;
mo[1][1]=bpow(10,k)%m,mo[1][2]=0,mo[1][3]=0;//单位矩阵初始化
mo[2][1]=1,mo[2][2]=1,mo[2][3]=0;
mo[3][1]=0,mo[3][2]=1,mo[3][3]=1;
ksm(len);
k++;
ll nans=(ans*mo[1][1]%m+l%m*mo[2][1]%m+mo[3][1]%m)%m;
//初始矩阵只有第一项有用,其它的可以舍弃
ans=nans;
}
cout<<ans;
return 0;
}
浙公网安备 33010602011771号