数学·线性代数
矩阵
运算法则
加法
两个矩阵 \(A\) 和 \(B\) 相加,要求它们的维度相同。结果矩阵 \(C=A+B\) 的每个元素是 \(A\) 和 \(B\) 对应元素的和。$$C_{i,j}=A_{i,j}+B_{i,j}$$
数乘
矩阵 \(A\) 与标量 \(k\) 相乘,结果矩阵 \(B=kA\) 的每个元素是 \(A\) 的对应元素乘以 \(k\)。$$B_{i,j}=k\cdot A_{i,j}$$
乘法
矩阵 \(A\) 与矩阵 \(B\) 相乘,要求 \(A\) 的列数等于 \(B\) 的行数。结果矩阵 \(C=AB\) 的每个元素是 \(A\) 的行向量与 \(B\) 的列向量的点积。$$C_{i,j}=\sum_{k=1}^nA_{i,k}\cdot B_{k,j}$$
注意:矩阵乘法不满足交换律
矩阵快速幂
其实和普通快速幂几乎一样。
我们要使用单位矩阵 \(I=\begin{bmatrix}
1&0&\cdots&0\\
0&1&\cdots&0\\
\vdots&\vdots&\ddots&\vdots\\
0&0&\cdots&1
\end{bmatrix}\) 作为初始矩阵。因为它乘任何矩阵还得跟它乘的那个矩阵,类似于 \(1\) 在普通快速幂中的作用。
#include<cstdio>
#include<cstring>
#define mod 1000000007
#define int long long
int n,k;
struct matrix{
int a[110][110];
matrix(){
memset(a,0,sizeof(a));
}
matrix operator*(const matrix &b)const{
matrix res;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
res.a[i][j]=(res.a[i][j]+a[i][k]*b.a[k][j]%mod)%mod;
return res;
}
}ans,base,a;
void init(){
for(int i=1;i<=n;i++)
base.a[i][i]=1;
return;
}
matrix qpow(matrix a,long long b){
matrix res=base;
while(b){
if(b&1)
res=res*a;
a=a*a;
b>>=1;
}
return res;
}
signed main(){
scanf("%lld%lld",&n,&k);
init();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%lld",&a.a[i][j]);
ans=qpow(a,k);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
printf("%lld ",ans.a[i][j]);
printf("\n");
}
return 0;
}
矩阵加速线性递推
例如斐波那契数列,\(F_1=F_2=1\),\(F_i=F_{i-1}+F_{i-2}\),矩阵递推形式为
则 \(F_n\) 就是 \(\begin{bmatrix}1&1\end{bmatrix} \begin{bmatrix}1&1\\ 1&0\end{bmatrix}^{n-2}\) 的第一行第一列的元素。用矩阵快速幂即可。
高斯消元
现有线性方程组
求解该方程组。
为了方便起见,我们用矩阵表示方程组:
首先消去除 \(1\) 式以外所有式子中的 \(x_1\),然后消去除 \(1\) 式和 \(2\) 式外所有式子中的 \(x_2\)……最后式子变为一个三角形的结构
之后再一点点代回求解,时间复杂度 \(O(n^3)\)。
对于无解的判断:某一行前 \(n\) 个数均为 \(0\),最后的结果却不为 \(0\)。
对于无数解的判断:某一行 \(n+1\) 个数均为 \(0\)。
模板
for(int i=1;i<=n;i++){
int r=i;
for(int j=i+1;j<=n;j++)
if(fabs(matrix[r][i])<fabs(matrix[j][i]))
r=j;//寻找主元
if(fabs(matrix[r][i])<eps){
printf("No Solution");//若主元为0,方程组无解或无穷多解
return 0;
}
if(i!=r)
swap(matrix[i],matrix[r]);
double div=matrix[i][i];
for(int j=i;j<=n+1;j++)
matrix[i][j]/=div;
for(int j=i+1;j<=n;j++){
div=matrix[j][i];
for(int k=i;k<=n+1;k++)
matrix[j][k]-=matrix[i][k]*div;
}//消元
}
ans[n]=matrix[n][n+1];
for(int i=n-1;i;i--){
ans[i]=matrix[i][n+1];
for(int j=i+1;j<=n;j++)
ans[i]-=matrix[i][j]*ans[j];
}//回代
for(int i=1;i<=n;i++){
int pivot=rank1+1;//rank1为矩阵的秩
for(int j=rank1+1;j<=n;j++)
if(fabs(matrix[pivot][i])<fabs(matrix[j][i]))
pivot=j;
if(fabs(matrix[pivot][i])<eps)
continue;//全为零
if(pivot!=rank1+1)
swap(matrix[pivot],matrix[rank1+1]);
double div=matrix[rank1+1][i];
for(int j=i;j<=n+1;j++)
matrix[rank1+1][j]/=div;
for(int j=1;j<=n;j++){
if(j==rank1+1||fabs(matrix[j][i])<eps)
continue;
div=matrix[j][i];
for(int k=i;k<=n+1;k++)
matrix[j][k]-=matrix[rank1+1][k]*div;
}//消元
rank1++;
}
for(int i=rank1+1;i<=n;i++)
if(fabs(matrix[i][n+1])>eps){
printf("-1");
return 0;//无解
}
if(rank1<n){
printf("0");
return 0;//无数解
}
for(int i=n;i;i--){
ans[i]=matrix[i][n+1];
for(int j=i+1;j<=n;j++)
ans[i]-=matrix[i][j]*ans[j];
}//回代
线性基
引入:有 \(n\) 个数,选出其中任意一些数,求其异或和,一共有多少种可能的异或值。
性质
- 原序列里面的任意一个数都可以表示为线性基的一个子集的异或和。
- 线性基任意一个子集的异或和都不能等于 \(0\)。
- 线性基里面的数的个数唯一,并且在保持性质 1 的前提下,数的个数是最少的。
struct LBase{
long long d[61];
LBase(){
memset(d,0,sizeof(d));
}
bool insert(long long x){
for(int i=60;i>=0;i--){
if(x&1ll<<i){
if(d[i])
x^=d[i];
else{
d[i]=x;
return 1;
}
}
}
return 0;
}
long long query_max(){
long long x=0;
for(int i=60;i>=0;i--)
if(d[i]&&(x^d[i])>x)
x^=d[i];
return x;
}
void merge(const LBase &a){
for(int i=60;i>=0;i--)
if(a.d[i])
insert(a.d[i]);
return;
}
};

浙公网安备 33010602011771号