D. 方阵的行列式
题意
给定一个模 998244353 (质数)意义下的 n × n n\times n n×n 的方阵和 Q Q Q 个修改操作。每个修改操作都修改方阵中某个元素为一个新的值。在每个修改操作后,输出方阵的行列式。
数据范围:
1 ≤ n , Q ≤ 500 1\le n,Q\le 500 1≤n,Q≤500
(修改第 x x x 行第 y y y 列的元素为 z z z) 1 ≤ x , y ≤ n , 0 ≤ z < 998244353 1\le x,y\le n,0\le z<998244353 1≤x,y≤n,0≤z<998244353。
题解
前置知识
-
Sherman-Morrison formula: 假设 A ∈ R n × n A\in\R^{n\times n} A∈Rn×n 是可逆方阵, u , v ∈ R n u,v\in\R^n u,v∈Rn 是列向量,则 A + u v T A+uv^T A+uvT 可逆当且仅当 1 + v T A − 1 u ≠ 0 1+v^TA^{-1}u\ne 0 1+vTA−1u=0,此时有
( A + u v T ) − 1 = A − 1 − A − 1 u v T A − 1 1 + v T A − 1 u (A+uv^T)^{-1}=A^{-1}-\frac{A^{-1}uv^TA^{-1}}{1+v^TA^{-1}u} (A+uvT)−1=A−1−1+vTA−1uA−1uvTA−1
成立 -
定义:在 n n n 阶行列式中,划去元 a i j a_{ij} aij 所在的第 i i i 行与第 j j j 列的元,剩下的元不改变原来的顺序所构成的 n − 1 n-1 n−1 阶行列式称为元 a i j a_{ij} aij 的余子式,记作 M i j M_{ij} Mij。
-
定义: a i j a_{ij} aij 的代数余子式: A i j = ( − 1 ) i + j M i j A_{ij}=(-1)^{i+j}M_{ij} Aij=(−1)i+jMij。
-
定理:当 ∣ A ∣ ≠ 0 |A|\ne 0 ∣A∣=0 时, A − 1 = 1 ∣ A ∣ A ∗ A^{-1}=\dfrac{1}{|A|}A^* A−1=∣A∣1A∗,其中 A ∗ A^* A∗ 的第 ( i , j ) (i,j) (i,j) 元为 A A A 的第 ( j , i ) (j,i) (j,i) 元的代数余子式 A j i A_{ji} Aji。 A ∗ A^* A∗ 称为 A A A 的伴随方阵。
-
展开定理:行列式 Δ \Delta Δ 的值,等于它的任意一行各元分别乘以各自的代数余子式的乘积之和:
Δ = ∑ j = 1 n a i j A i j . \Delta=\sum_{j=1}^na_{ij}A_{ij}. Δ=j=1∑naijAij.
注意:在按行展开的公式
Δ = a i 1 A i 1 + ⋯ + a i j A i j + ⋯ + a i n A i n \Delta=a_{i1}A_{i1}+\cdots+a_{ij}A_{ij}+\cdots+a_{in}A_{in} Δ=ai1Ai1+⋯+aijAij+⋯+ainAin
中,第 i i i 行的各元的代数余子式 A i 1 , ⋯ , A i n A_{i1},\cdots,A_{in} Ai1,⋯,Ain 的值都由 Δ \Delta Δ 中第 i i i 行以外的元决定,与第 i i i 行各元 a i 1 , ⋯ , a i n a_{i1},\cdots,a_{in} ai1,⋯,ain 无关。因此,在行列式 Δ \Delta Δ 中将第 i i i 行各元分别换成任意值 x 1 , ⋯ , x n x_1,\cdots,x_n x1,⋯,xn,得到的行列式 Δ ( x 1 , ⋯ , x n ) \Delta(x_1,\cdots,x_n) Δ(x1,⋯,xn) 按第 i i i 行展开式中的各代数余子式 A i 1 , ⋯ , A i n A_{i1},\cdots,A_{in} Ai1,⋯,Ain 仍与在 Δ \Delta Δ 中相同。
算法步骤
- 高斯消元法求 A − 1 A^{-1} A−1 和 ∣ A ∣ |A| ∣A∣,记 A − 1 = C = ( c i j ) A^{-1}=C=(c_{ij}) A−1=C=(cij),复杂度 O ( n 3 ) O(n^3) O(n3)。
- 执行修改操作,将 ( x , y ) (x,y) (x,y) 元修改为 z z z,改变量记为 d e l t a = z − a x y delta=z-a_{xy} delta=z−axy,改变后的矩阵记为 A ′ = ( a i j ′ ) A'=(a'_{ij}) A′=(aij′)。
- 计算修改后的行列式的值 ∣ A ′ ∣ |A'| ∣A′∣,用定理 5 对第 x x x 行按行展开来求,而由定理 4, A i j ′ = A i j = ∣ A ∣ c j i A'_{ij}=A_{ij}=|A|c_{ji} Aij′=Aij=∣A∣cji,故 ∣ A ′ ∣ = ∑ j = 1 n a x j ′ ∣ A ∣ c j i \displaystyle |A'|=\sum_{j=1}^na'_{xj}|A|c_{ji} ∣A′∣=j=1∑naxj′∣A∣cji。
- 令 ∣ A ∣ = ∣ A ′ ∣ |A|=|A'| ∣A∣=∣A′∣,并用定理 1 计算 ( A ′ ) − 1 (A')^{-1} (A′)−1:令 u = ( 0 , ⋯ , 0 , 1 , 0 , ⋯ , 0 ) T u=(0,\cdots,0,1,0,\cdots,0)^T u=(0,⋯,0,1,0,⋯,0)T(第 x x x 个分量为 1,其它分量为 0 的列向量), v = ( 0 , ⋯ , 0 , d e l t a , 0 , ⋯ , 0 ) T v=(0,\cdots,0,delta,0,\cdots,0)^T v=(0,⋯,0,delta,0,⋯,0)T (第 y y y 个分量为 d e l t a delta delta,其它分量为 0 的列向量),即可在 O ( n 2 ) O(n^2) O(n2) 内算出 ( A ′ ) − 1 = ( A + u v T ) = A − 1 − A − 1 u v T A − 1 1 + v T A − 1 u (A')^{-1}=(A+uv^T)=A^{-1}-\dfrac{A^{-1}uv^TA^{-1}}{1+v^TA^{-1}u} (A′)−1=(A+uvT)=A−1−1+vTA−1uA−1uvTA−1。然后令 C = A − 1 = ( A ′ ) − 1 , A = A ′ C=A^{-1}=(A')^{-1},A=A' C=A−1=(A′)−1,A=A′。回到步骤 2,进行下一步修改操作,直至修改操作结束。
故总时间复杂度 O ( n 3 + Q n 2 ) O(n^3+Qn^2) O(n3+Qn2)。
#include<bits/stdc++.h>
using namespace std;
const int N=505,mod=998244353;
int A[N][N],B[N][2*N],C[N][N],D[N][N],E[N][N],F[N][N];
int qpow(int x,int y){
int ret=1;
for(;y;x=1ll*x*x%mod,y>>=1)
if(y&1) ret=1ll*ret*x%mod;
return ret;
}
void Get_Inverse_1(int n,int& det){
det=1;
memset(B,0,sizeof(B));
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) B[i][j]=A[i][j];
for(int i=1;i<=n;i++) B[i][i+n]=1;
int times=0;
for(int i=1;i<n;i++){
if(B[i][i]==0){
times++;
int row=-1;
for(int j=i+1;j<=n;j++)
if(B[j][i]!=0){
row=j;
break;
}
if(row==-1) continue;
for(int k=i;k<=2*n;k++) swap(B[i][k],B[row][k]);
}
for(int j=i+1;j<=n;j++){
int m=1ll*B[j][i]*qpow(B[i][i],mod-2)%mod;
for(int k=i;k<=2*n;k++){
B[j][k]=(1ll*B[j][k]-1ll*m*B[i][k]%mod+mod)%mod;
}
}
det=1ll*det*B[i][i]%mod;
}
det=1ll*det*B[n][n]%mod;
if(times&1) det=(mod-det)%mod;
for(int i=n;i>=1;i--){
int temp=B[i][i];
temp=qpow(temp,mod-2);
B[i][i]=1;
for(int k=n+1;k<=2*n;k++) B[i][k]=1ll*B[i][k]*temp%mod;
int temp2=qpow(B[i][i],mod-2);
for(int j=i-1;j>=1;j--){
int m=1ll*B[j][i]*temp2;
for(int k=n+1;k<=2*n;k++){
B[j][k]=(1ll*B[j][k]-1ll*m*B[i][k]%mod+mod)%mod;
}
}
}
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) C[i][j]=B[i][j+n];
}
int C2[N],C3[N];
void Get_Inverse_2(int n,int x,int y,int delta){
int deno=(1+1ll*C[y][x]*delta%mod)%mod;
deno=qpow(deno,mod-2);
for(int i=1;i<=n;i++) C2[i]=C[i][x],C3[i]=C[y][i];
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
C[i][j]=(1ll*C[i][j]-1ll*C2[i]*delta%mod*C3[j]%mod*deno%mod+mod)%mod;
}
}
int main(){
int n,q;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&A[i][j]);
int det;
Get_Inverse_1(n,det);
while(q--){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
int before=A[x][y];
A[x][y]=z;
int ans=0;
for(int j=1;j<=n;j++){
ans=(1ll*ans+1ll*A[x][j]*C[j][x]%mod*det%mod)%mod;
}
printf("%d\n",ans);
det=ans;
int delta=(z-before+mod)%mod;
Get_Inverse_2(n,x,y,delta);
}
return 0;
}