算法总结—矩阵加速

矩阵

矩阵类似于二维数组:

\(A=\begin{bmatrix}a&b\\ c&d\end{bmatrix}\)

矩阵的用途

  • 解方程。

  • 加速线性 DP。

  • 加速 Floyd。

常数乘法

\(Ak=\begin{bmatrix}ak&bk\\ ck&dk\end{bmatrix}\)

矩阵乘法

\(A=\begin{bmatrix}a&b\\ c&d\end{bmatrix}\)\(B=\begin{bmatrix}x&y&z\\ u&v&w\end{bmatrix}\)

\(C=A\times B=\begin{bmatrix}ax+bu&ay+bv&az+bw\\ cx+du&cy+dv&cz+dw\end{bmatrix}\)

也就是 \(C_{i,j}=\sum_{i=1}^{A\tt{的列数}} A_{i,k}B_{k,j}\),所以在矩阵乘法中,\(A\) 的列数要等与 \(B\) 的行数。

单位矩阵

单位矩阵就是形如 \(\begin{bmatrix}1&0&\cdots&0\\0&1&\cdots&0\\\vdots&\vdots&\ddots&\vdots\\0&0&\cdots&1\end{bmatrix}\) 的矩阵,任何矩阵乘单位矩阵都等于自己,就相当于整数中的 \(1\)

矩阵快速幂

矩阵快速幂与整数快速幂类似,唯一的不同是需要重载一下矩阵的乘法。

【模板】矩阵快速幂

快速幂

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
int n,p;
struct matrix{
	int n,m;
	int a[105][105];
	matrix():n(0),m(0){
		memset(a,0,sizeof(a));
		return;
	}
	matrix(int x,int y):n(x),m(y){
		memset(a,0,sizeof(a));
		return;
	}
	matrix operator*(const matrix&b){
		matrix c(n,b.m);
		for(int i=1;i<=n;i++){
			for(int j=1;j<=b.m;j++){
				for(int k=1;k<=m;k++){
					c.a[i][j]=(c.a[i][j]+a[i][k]*b.a[k][j])%mod;
				}
			}
		}
		return c;
	}
};
matrix qpow(matrix a,int p){
	matrix ans(n,n);
    for(int i=1;i<=n;i++){
        ans.a[i][i]=1;
    }
	while(p){
		if(p&1){
			ans=ans*a;
		}
		a=a*a;
		p/=2;
	}
	return ans;
}
signed main(){
	cin>>n>>p;
    matrix A(n,n);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>A.a[i][j];
		}
	}
	matrix ans=qpow(A,p);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cout<<ans.a[i][j]<<" ";
		}
		cout<<"\n";
	}
	return 0;
}

广义斐波那契数列

可以把 \(\begin{bmatrix}a_1&a_2\end{bmatrix}\) 看作初始的矩阵,想 \(\begin{bmatrix}a_1&a_2\end{bmatrix}\) 乘上什么矩阵等于 \(\begin{bmatrix}a_2&a_3\end{bmatrix}\)

首先看 \(a_1\times?+a_2\times?=a_2\)\(a_1\times 0+a_2\times 1=a_2\)

再看 \(a_1\times?+a_2\times?=a_3\)\(a_1\times q+a_2\times p=a_3\)

所以 \(\begin{bmatrix}a_1&a_2\end{bmatrix}\times\begin{bmatrix}0&q\\1&p\end{bmatrix}=\begin{bmatrix}a_2&a_3\end{bmatrix}\)

那么 \(\begin{bmatrix}a_n&a_{n+1}\end{bmatrix}=\begin{bmatrix}a_1&a_2\end{bmatrix}\times\begin{bmatrix}0&q\\1&p\end{bmatrix}^{n-1}\)

而矩阵又可以进行快速幂,所以 \(a_n\) 就可以在 \(\mathcal{O}(2^3\times \log n)\) 的时间内算出来了。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int p,q,x,y,n,mod;
struct matrix{
	int n,m;
	int a[5][5];
	matrix():n(0),m(0){
		memset(a,0,sizeof(a));
		return;
	}
	matrix(int x,int y):n(x),m(y){
		memset(a,0,sizeof(a));
		return;
	}
	matrix operator*(const matrix&b){
		matrix c(n,b.m);
		for(int i=1;i<=n;i++){
			for(int j=1;j<=b.m;j++){
				for(int k=1;k<=m;k++){
					c.a[i][j]=(c.a[i][j]+a[i][k]*b.a[k][j])%mod;
				}
			}
		}
		return c;
	}
};
matrix qpow(matrix a,int p){
    matrix ans(2,2);
    for(int i=1;i<=2;i++){
        ans.a[i][i]=1;
    }
    while(p){
        if(p&1){
            ans=ans*a;
        }
        a=a*a;
        p/=2;
    }
    return ans;
}
signed main(){
    cin>>p>>q>>x>>y>>n>>mod;
    if(n==1){
        cout<<x;
        return 0;
    }
    if(n==2){
        cout<<y;
        return 0;
    }
    matrix A(1,2);
    A.a[1][1]=x,A.a[1][2]=y;
    matrix B(2,2);
    B.a[1][1]=0,B.a[1][2]=q;
    B.a[2][1]=1,B.a[2][2]=p;
    matrix C=qpow(B,n-1);
    A=A*C;
    cout<<A.a[1][1];
    return 0;
}

骨牌

这道题显然是 DP。

\(dp_1=1\)

\(dp_2=2\)

\(dp_3=4\)

\(dp_i=dp_{i-1}+dp_{i-2}+dp_{i-3}(i\geq 4)\)

但是 \(n\) 可以达到 \(10^{18}\),所以不能 \(\mathcal{O}(n)\) 写,但是可以用矩阵加速。

可以把 \(\begin{bmatrix}dp_1&dp_2&dp_3\end{bmatrix}\) 看作初始的矩阵,想 \(\begin{bmatrix}dp_1&dp_2&dp_3\end{bmatrix}\) 乘上什么矩阵等于 \(\begin{bmatrix}dp_2&dp_3&dp_4\end{bmatrix}\)

首先看 \(dp_1\times?+dp_2\times?+dp_3\times?=dp_2\)\(dp_1\times0+dp_2\times1+dp_3\times0=dp_2\)

再看 \(dp_1\times?+dp_2\times?+dp_3\times?=dp_3\)\(dp_1\times0+dp_2\times0+dp_3\times1=dp_3\)

最后看 \(dp_1\times?+dp_2\times?+dp_3\times?=dp_4\)\(dp_1\times1+dp_2\times1+dp_3\times1=dp_4\)

所以 \(\begin{bmatrix}dp_1&dp_2&dp_3\end{bmatrix}\times\begin{bmatrix}0&0&1\\1&0&1\\0&1&1\end{bmatrix}=\begin{bmatrix}dp_2&dp_3&dp_4\end{bmatrix}\)

那么 \(\begin{bmatrix}dp_n&dp_{n+1}&dp_{n+2}\end{bmatrix}=\begin{bmatrix}dp_1&dp_2&dp_3\end{bmatrix}\times\begin{bmatrix}0&0&1\\1&0&1\\0&1&1\end{bmatrix}^{n-1}\)

这样,所以 \(dp_n\) 就可以在 \(\mathcal{O}(3^3\times \log n)\) 的时间内算出来了。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
struct matrix{
	int n,m;
	int a[5][5];
	matrix():n(0),m(0){
		memset(a,0,sizeof(a));
		return;
	}
	matrix(int x,int y):n(x),m(y){
		memset(a,0,sizeof(a));
		return;
	}
	matrix operator*(const matrix&b){
		matrix c(n,b.m);
		for(int i=1;i<=n;i++){
			for(int j=1;j<=b.m;j++){
				for(int k=1;k<=m;k++){
					c.a[i][j]=(c.a[i][j]+a[i][k]*b.a[k][j])%mod;
				}
			}
		}
		return c;
	}
};
matrix qpow(matrix a,int p){
	matrix ans(3,3);
	for(int i=1;i<=3;i++){
		ans.a[i][i]=1;
	}
	while(p){
		if(p&1){
			ans=ans*a;
		}
		a=a*a;
		p/=2;
	}
	return ans;
}
signed main(){
	int n;
	cin>>n;
	matrix A(1,3);
	A.a[1][1]=1,A.a[1][2]=2,A.a[1][3]=4;
	matrix B(3,3);
	B.a[1][3]=B.a[2][1]=B.a[2][3]=B.a[3][2]=B.a[3][3]=1;
	matrix C=qpow(B,n-1);
    A=A*C;
	cout<<A.a[1][1];
	return 0;
}

[NOI2012] 随机数生成器

初始矩阵 \(\begin{bmatrix}X_0&c\end{bmatrix}\),根据上面的方法 \(\begin{bmatrix}X_0&c\end{bmatrix}\times\begin{bmatrix}a&0\\1&1\end{bmatrix}=\begin{bmatrix}X_1&c\end{bmatrix}\)\(\begin{bmatrix}X_n&c\end{bmatrix}=\begin{bmatrix}X_0&c\end{bmatrix}\times\begin{bmatrix}a&0\\1&1\end{bmatrix}^n\)

但这道题很坑,\(X_0,m\leq 10^{18}\),矩阵相乘时,会爆 long long ,所以要用到龟速乘,边乘边取模,龟速乘的时间复杂度是 \(\log\) 级别的,代码如下:

int qmul(int a,int p){
	int ans=0;
	while(p){
		if(p&1){
			ans=(ans+a)%mod;
		}
		a=(a+a)%mod;
		p/=2;
	}
	return ans;
}

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int mod,a,c,x0,n,g;
int qmul(int a,int p){
	int ans=0;
	while(p){
		if(p&1){
			ans=(ans+a)%mod;
		}
		a=(a+a)%mod;
		p/=2;
	}
	return ans;
}
struct matrix{
	int n,m;
	int a[5][5];
	matrix():n(0),m(0){
		memset(a,0,sizeof(a));
		return;
	}
	matrix(int x,int y):n(x),m(y){
		memset(a,0,sizeof(a));
		return;
	}
	matrix operator*(const matrix&b){
		matrix c(n,b.m);
		for(int i=1;i<=n;i++){
			for(int j=1;j<=b.m;j++){
				for(int k=1;k<=m;k++){
					c.a[i][j]=(c.a[i][j]+qmul(a[i][k],b.a[k][j]))%mod;
				}
			}
		}
		return c;
	}
};
matrix qpow(matrix a,int p){
	matrix ans(2,2);
	for(int i=1;i<=2;i++){
		ans.a[i][i]=1;
	}
	while(p){
		if(p&1){
			ans=ans*a;
		}
		a=a*a;
		p/=2;
	}
	return ans;
}
signed main(){
	cin>>mod>>a>>c>>x0>>n>>g;
	matrix A(1,2);
	A.a[1][1]=x0,A.a[1][2]=c;
	matrix B(2,2);
	B.a[1][1]=a,B.a[1][2]=0;
	B.a[2][1]=1,B.a[2][2]=1;
	matrix C=qpow(B,n);
	A=A*C;
	cout<<A.a[1][1]%g;
	return 0;
}
posted @ 2025-03-31 13:00  LRRabcd  阅读(106)  评论(0)    收藏  举报