【XSY2777】特征多项式
给一个没有特殊性质的矩阵 \(A\),求其特征多项式 \(|\lambda I-A|\)。
定义:若存在一可逆矩阵 \(P\),使得 \(B=PAP^{-1}\),那么称 \(A\) 与 \(B\) 相似,记为 \(A\sim B\)。
相似矩阵有很多特殊性质,比如相似矩阵的特征多项式相同。
证明:
首先有 \(\lambda I=\lambda PIP^{-1}=P\lambda IP^{-1}\)。
\[\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} \]
那么我们对 \(A\) 先进行行变换,记这个行变换对应的初等矩阵为 \(P\),那么我们的操作就是对 \(A\) 左乘 \(P\)。
然后我们再对 \(PA\) 右乘 \(P^{-1}\),对应的操作是列变换,然后就能得到 \(B=PAP^{-1}\),显然它与 \(A\) 相似。
具体地说:
- 若交换矩阵 \(A\) 的第 \(i\) 行和第 \(j\) 行,对应的是交换矩阵 \(A\) 的第 \(i\) 列和第 \(j\) 列。
- 若将矩阵 \(A\) 的第 \(i\) 行乘上常数 \(k\),对应的是将矩阵 \(A\) 的第 \(i\) 列乘上常数 \(-k\)。
- 若将矩阵 \(A\) 的第 \(i\) 行加上 \(k\) 乘第 \(j\) 行,对应的是将矩阵 \(A\) 的第 \(j\) 列加上 \(-k\) 乘第 \(i\) 列。
推导方式是通过行变换的内容得到左乘的初等矩阵 \(P\),再得到 \(P^{-1}\),再得到右乘 \(P^{-1}\) 的对应的列变换。
但我们不能通过这种方式把 \(A\) 变成上三角矩阵,因为我们在消掉 \((j,i)\) 这个位置的时候,需要让 \(A\) 的第 \(j\) 行加上 \(k\) 乘第 \(i\) 行,此时 \((j,i)\) 变成了 \(0\)。但对应地,我们需要将矩阵 \(A\) 的第 \(i\) 列加上 \(-k\) 乘第 \(j\) 列,这样 \((j,i)\) 又会被受到影响,可能不是 \(0\) 了。
所以我们在消掉 \((j,i)\) 这个位置的时候,用 \(A\) 的第 \(i+1\) 行与第 \(j\) 行做行变换来先把 \((j,i)\) 消掉,这样对应的是 \(A\) 的第 \(i+1\) 列与第 \(j\) 列做列变换,就不会影响到已经消掉的 \((j,i)\) 了。
那么最后得到的就不是上三角矩阵,而是上海森堡矩阵(断句:上/海森堡矩阵):
那么 \(\lambda I -A\) 为:
设 \(f_i\) 表示右下角 \(i\times 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;
}