线性代数

线性代数

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;
}
posted @ 2025-06-02 11:36  金牌白鸽  阅读(39)  评论(0)    收藏  举报