题解:P4783 【模板】矩阵求逆
a_small_OIer · · 题解
题目传送门
题目要求
要求给定一个矩阵
解法
首先给出逆矩阵的定义:若对于矩阵
:::info[
单位矩阵具有一些很好的性质,下面给出三个例子:
-
\left | I \right |=1 -
I\times A=A -
A\times I=A
(注意矩阵乘法不满足交换律,所以上面两种写法是不等价的)
读者可以试着证明以上几条性质,也可以自行搜索相关知识。 :::
求一个矩阵
:::info[简单介绍伴随矩阵解法] 定义矩阵
那么它的伴随矩阵
(其中
所以我们可以对一个矩阵先求代数余子式再转置,得到
可以得到
但是在
高斯 - 约旦消元法
我们构造
所以我们可以先构造增广矩阵
消元的具体操作:
- 从左到右逐列处理,设当前列号为
c 。 - 在
[c, n] 行中寻找第c 列元素非零的行,若找不到则不存在A 的逆矩阵A^{-1} ,即矩阵不可逆,无解。 - 将该行交换到第
c 行。 - 将当前行的第
c 列元素通过乘以逆元(利用费马小定理即可)化为1 。 - 用当前行消去其他所有行的第
c 列元素,使其变为0 。 - 继续处理下一列。
时间复杂度
AC 代码
#include <bits/stdc++.h>
#define N 410
#define P 1000000007
using namespace std;
typedef long long LL;
//十年OI一场空,不开long long见祖宗
LL qp(LL a , LL b){
LL res = 1;
while(b){
if(b & 1)
res *= a,res %= P;
a = a * a % P;
b >>= 1;
}
return res;
}
LL inv(LL a){
return qp(a , P - 2);
}
int n;
LL a[N][N * 2];
int main(){
scanf("%d" , &n);
for(int i = 1 ; i <= n ; ++i){
for(int j = 1 ; j <= n ; ++j)
scanf("%lld" , &a[i][j]);
a[i][n + i] = 1;
}
for(int c = 1 ; c <= n ; ++c){
int r = c;
while(r <= n && a[r][c] == 0)
r += 1;
if(r > n){
printf("No Solution");
return 0;
}
if(r != c)
for(int i = 1 ; i <= 2 * n ; ++i)
swap(a[c][i] , a[r][i]);
LL inv_ = inv(a[c][c]);
for(int i = 1 ; i <= 2 * n ; ++i)
a[c][i] *= inv_ , a[c][i] %= P;
for(int i = 1 ; i <= n ; ++i){
if(i == c || a[i][c] == 0)
continue;
LL num = a[i][c];
for(int j = 1 ; j <= 2 * n ; ++j){
a[i][j] = (a[i][j] - num * a[c][j]) % P;
if(a[i][j] < 0)
a[i][j] += P;
}
}
}
for(int i = 1 ; i <= n ; ++i){
for(int j = 1 ; j <= n ; ++j)
printf("%lld " , a[i][n + j]);
printf("\n");
}
return 0;
}