给一个没有特殊性质的矩阵 A A A,求其特征多项式 ∣ λ I − A ∣ |\lambda I-A| ∣λI−A∣。
定义:若存在一可逆矩阵 P P P,使得 B = P A P − 1 B=PAP^{-1} B=PAP−1,那么称 A A A 与 B B B 相似,记为 A ∼ B A\sim B A∼B。
相似矩阵有很多特殊性质,比如相似矩阵的特征多项式相同。
证明:
首先有 λ I = λ P I P − 1 = P λ I P − 1 \lambda I=\lambda PIP^{-1}=P\lambda IP^{-1} λI=λPIP−1=PλIP−1。
det ( B ) = ∣ λ I − B ∣ = ∣ λ I − P A P − 1 ∣ = ∣ P λ I P − 1 − P A P − 1 ∣ = ∣ P ( λ I − A ) P − 1 ∣ = ∣ P ∣ × ∣ λ I − A ∣ × ∣ P − 1 ∣ = ∣ P × P − 1 ∣ × det ( A ) = det ( A ) \begin{aligned} \det(B)&=|\lambda I -B|\\ &=|\lambda I -PAP^{-1}|\\ &=|P\lambda IP^{-1}-PAP^{-1}|\\ &=|P(\lambda I-A)P^{-1}|\\ &=|P|\times |\lambda I -A|\times |P^{-1}|\\ &=|P\times P^{-1}|\times \det(A)=\det(A) \end{aligned} det(B)=∣λI−B∣=∣λI−PAP−1∣=∣PλIP−1−PAP−1∣=∣P(λI−A)P−1∣=∣P∣×∣λI−A∣×∣P−1∣=∣P×P−1∣×det(A)=det(A)
那么我们对 A A A 先进行行变换,记这个行变换对应的初等矩阵为 P P P,那么我们的操作就是对 A A A 左乘 P P P。
然后我们再对 P A PA PA 右乘 P − 1 P^{-1} P−1,对应的操作是列变换,然后就能得到 B = P A P − 1 B=PAP^{-1} B=PAP−1,显然它与 A A A 相似。
具体地说:
- 若交换矩阵 A A A 的第 i i i 行和第 j j j 行,对应的是交换矩阵 A A A 的第 i i i 列和第 j j j 列。
- 若将矩阵 A A A 的第 i i i 行乘上常数 k k k,对应的是将矩阵 A A A 的第 i i i 列乘上常数 − k -k −k。
- 若将矩阵 A A A 的第 i i i 行加上 k k k 乘第 j j j 行,对应的是将矩阵 A A A 的第 j j j 列加上 − k -k −k 乘第 i i i 列。
推导方式是通过行变换的内容得到左乘的初等矩阵 P P P,再得到 P − 1 P^{-1} P−1,再得到右乘 P − 1 P^{-1} P−1 的对应的列变换。
但我们不能通过这种方式把 A A A 变成上三角矩阵,因为我们在消掉 ( j , i ) (j,i) (j,i) 这个位置的时候,需要让 A A A 的第 j j j 行加上 k k k 乘第 i i i 行,此时 ( j , i ) (j,i) (j,i) 变成了 0 0 0。但对应地,我们需要将矩阵 A A A 的第 i i i 列加上 − k -k −k 乘第 j j j 列,这样 ( j , i ) (j,i) (j,i) 又会被受到影响,可能不是 0 0 0 了。
所以我们在消掉 ( j , i ) (j,i) (j,i) 这个位置的时候,用 A A A 的第 i + 1 i+1 i+1 行与第 j j j 行做行变换来先把 ( j , i ) (j,i) (j,i) 消掉,这样对应的是 A A A 的第 i + 1 i+1 i+1 列与第 j j j 列做列变换,就不会影响到已经消掉的 ( j , i ) (j,i) (j,i) 了。
那么最后得到的就不是上三角矩阵,而是上海森堡矩阵(断句:上/海森堡矩阵):
[
a
1
,
1
a
1
,
2
a
1
,
3
⋯
a
1
,
n
−
1
a
1
,
n
a
2
,
1
a
2
,
2
a
2
,
3
⋯
a
2
,
n
−
1
a
2
,
n
0
a
3
,
2
a
3
,
3
⋯
a
3
,
n
−
1
a
3
,
n
0
0
a
4
,
3
⋯
a
4
,
n
−
1
a
4
,
n
⋮
⋮
⋮
⋱
⋮
⋮
0
0
0
⋯
a
n
,
n
−
1
a
n
,
n
]
\begin{bmatrix} a_{1,1}&a_{1,2}&a_{1,3}&\cdots&a_{1,n-1}&a_{1,n}\\ a_{2,1}&a_{2,2}&a_{2,3}&\cdots&a_{2,n-1}&a_{2,n}\\ 0&a_{3,2}&a_{3,3}&\cdots&a_{3,n-1}&a_{3,n}\\ 0&0&a_{4,3}&\cdots&a_{4,n-1}&a_{4,n}\\ \vdots&\vdots&\vdots&\ddots&\vdots&\vdots\\ 0&0&0&\cdots&a_{n,n-1}&a_{n,n}\\ \end{bmatrix}
⎣⎢⎢⎢⎢⎢⎢⎢⎡a1,1a2,100⋮0a1,2a2,2a3,20⋮0a1,3a2,3a3,3a4,3⋮0⋯⋯⋯⋯⋱⋯a1,n−1a2,n−1a3,n−1a4,n−1⋮an,n−1a1,na2,na3,na4,n⋮an,n⎦⎥⎥⎥⎥⎥⎥⎥⎤
那么
λ
I
−
A
\lambda I -A
λI−A 为:
[
λ
−
a
1
,
1
−
a
1
,
2
−
a
1
,
3
⋯
−
a
1
,
n
−
1
−
a
1
,
n
−
a
2
,
1
λ
−
a
2
,
2
−
a
2
,
3
⋯
−
a
2
,
n
−
1
−
a
2
,
n
0
−
a
3
,
2
λ
−
a
3
,
3
⋯
−
a
3
,
n
−
1
−
a
3
,
n
0
0
−
a
4
,
3
⋯
−
a
4
,
n
−
1
−
a
4
,
n
⋮
⋮
⋮
⋱
⋮
⋮
0
0
0
⋯
−
a
n
,
n
−
1
λ
−
a
n
,
n
]
\begin{bmatrix} \lambda-a_{1,1}&-a_{1,2}&-a_{1,3}&\cdots&-a_{1,n-1}&-a_{1,n}\\ -a_{2,1}&\lambda-a_{2,2}&-a_{2,3}&\cdots&-a_{2,n-1}&-a_{2,n}\\ 0&-a_{3,2}&\lambda-a_{3,3}&\cdots&-a_{3,n-1}&-a_{3,n}\\ 0&0&-a_{4,3}&\cdots&-a_{4,n-1}&-a_{4,n}\\ \vdots&\vdots&\vdots&\ddots&\vdots&\vdots\\ 0&0&0&\cdots&-a_{n,n-1}&\lambda-a_{n,n}\\ \end{bmatrix}
⎣⎢⎢⎢⎢⎢⎢⎢⎡λ−a1,1−a2,100⋮0−a1,2λ−a2,2−a3,20⋮0−a1,3−a2,3λ−a3,3−a4,3⋮0⋯⋯⋯⋯⋱⋯−a1,n−1−a2,n−1−a3,n−1−a4,n−1⋮−an,n−1−a1,n−a2,n−a3,n−a4,n⋮λ−an,n⎦⎥⎥⎥⎥⎥⎥⎥⎤
设
f
i
f_i
fi 表示右下角
i
×
i
i\times i
i×i 大小矩阵的行列式,然后就是分类讨论递推了,类似递推方法可以见这道题。
而且递推时只涉及多项式加法和多次乘一次的多项式乘法,于是直接暴力存储和运算多项式即可。
#include<bits/stdc++.h>
#define N 510
using namespace std;
namespace modular
{
const int mod=998244353;
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
}using namespace modular;
inline int poww(int a,int b)
{
int ans=1;
while(b)
{
if(b&1) ans=mul(ans,a);
a=mul(a,a);
b>>=1;
}
return ans;
}
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
typedef vector<int> poly;
poly add(poly &a,poly &b,int d)
{
poly c;
int sa=a.size(),sb=b.size();
for(int i=0;i<min(sa,sb);i++)
c.push_back(add(a[i],mul(d,b[i])));
if(sa>sb)
{
for(int i=sb;i<sa;i++)
c.push_back(a[i]);
}
else
{
for(int i=sa;i<sb;i++)
c.push_back(mul(d,b[i]));
}
return c;
}
poly mul(poly &a,poly &b)
{
int sa=a.size(),sb=b.size();
poly c(sa+sb-1);
for(int i=0;i<sa;i++)
for(int j=0;j<sb;j++)
c[i+j]=add(c[i+j],mul(a[i],b[j]));
return c;
}
int n,a[N][N];
poly f[N];
void Gauss()
{
for(int i=1;i<n;i++)
{
int p=i+1;
for(int j=i+1;j<=n;j++)
if(a[j][i]>a[p][i]) p=j;
if(p!=i+1)
{
for(int j=1;j<=n;j++)
swap(a[i+1][j],a[p][j]);
for(int j=1;j<=n;j++)
swap(a[j][i+1],a[j][p]);
}
for(int j=i+2;j<=n;j++)
{
int div=mul(a[j][i],poww(a[i+1][i],mod-2));
for(int k=1;k<=n;k++)
a[j][k]=dec(a[j][k],mul(a[i+1][k],div));
for(int k=1;k<=n;k++)
a[k][i+1]=add(a[k][i+1],mul(a[k][j],div));
}
}
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[i][j]=read();
Gauss();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[i][j]=dec(0,a[i][j]);
f[n+1].push_back(1);
for(int i=n;i>=1;i--)
{
poly tmp;
tmp.push_back(a[i][i]);
tmp.push_back(1);
f[i]=mul(tmp,f[i+1]);
int now=a[i+1][i],c=dec(0,1);
for(int j=i+1;j<=n;j++)
{
f[i]=add(f[i],f[j+1],mul(mul(c,now),a[i][j]));
now=mul(now,a[j+1][j]);
c=dec(0,c);
}
}
for(int i=0;i<=n;i++)
printf("%d ",f[1][i]);
return 0;
}