hdu 7253

显然的 \(\mathcal{O}(n^4)\) 做法是,Laplace 矩阵 \(L=D^{in}(G)-A(G)\)。由矩阵树定理可以知道 \(t^{leaf}(G,k)=\det M_{i,i}\)。根据 \(L\) 的生成方式可以知道,\(\sum_{i=1}^n L_{i,j}=0,\forall j\)

下面介绍一点点线性代数。


一个 \(n\times m\) 的矩阵的秩 \(rank\le \min(n,m)\),并且行秩等于列秩。

如果 \(n\times n\) 的矩阵 \(D\)\(rank\)

  • \(=n\),则 \(\det D\neq 0\)
  • \(=n-1\),则存在一个 \((n-1)\times (n-1)\) 的余子式 \(\det\neq 0\)
  • \(<n-1\),则所有\((n-1)\times (n-1)\) 的余子式 \(\det= 0\)

\(rank(D)\) 也等于 \(D\) 中最大线性无关向量个数。如果一组向量 \((\alpha_1,\alpha_2,\cdots,\alpha_k)\) 线性相关,当且仅当存在 $ (x_1,x_2,\cdots,x_k)\neq (0,0,\cdots ,0)$ 使得 \(\sum x_i\alpha_i=(0,0,\cdots ,0)\)

\(M_{i,j}\) 为矩阵 \(a\)\(a_{i,j}\) 的余子式,也就是 \(A\) 去掉第 \(i\) 行第 \(j\) 列的 \(n-1\) 阶矩阵的行列式。

\(A_{i,j}\) 为矩阵 \(a\)\(a_{i,j}\) 的代数余子式,\(A_{i,j}=(-1)^{i+j}M_{i,j}\)

矩阵 \(a\) 的伴随矩阵

\[A^*=\begin{pmatrix} A_{1,1} & A_{2,1} & \cdots & A_{n,1} \\ A_{1,2} & A_{2,2} & \cdots & A_{n,2} \\ \vdots & \vdots & \ddots &\vdots \\ A_{1,n} & A_{2,n} & \cdots & A_{n,n} \end{pmatrix} \]

其实,伴随矩阵对于这个题的作用不是很大,而是可以启发思考。

伴随矩阵每一列相等等价于,删除每一行是等价的,除了一个 \((-1)^{i+j}\) 的系数(看定义)。


因为 \(\sum_{i=1}^n L_{i,j}=0,\forall j\),所以 \(rank(L)\neq n\)

如果 \(rank(L)<n-1\),输出 \(n\)\(0\) 即可。

以下 \(rank(L)=n-1\)。尝试证明:\(A^*\) 中每一列相同。不妨只证明 \(A_{1,1}=A_{2,1}\),其他的有类似的证明。


命题:\(A_{1,1}=A_{2,1}\),且 \(\sum_{i=1}^n a_{i,j}=0,\forall j=1,2,\cdots, n\)

\[A_{2,1}=\det \begin{pmatrix} a_{1,2} & a_{1,3} & \cdots & a_{1,n} \\ a_{3,2} & a_{3,3} & \cdots & a_{3,n} \\ \vdots & \vdots & \ddots &\vdots \\ a_{n,2} & a_{n,3} & \cdots & a_{n,n} \end{pmatrix}\times (-1)= \det \begin{pmatrix} -a_{2,2}-a_{3,2}-\cdots -a_{n,2} & -a_{2,3}-\cdots -a_{n,3} & \cdots & -a_{2,n}-\cdots -a_{n,n} \\ a_{3,2} & a_{3,3} & \cdots & a_{3,n} \\ \vdots & \vdots & \ddots &\vdots \\ a_{n,2} & a_{n,3} & \cdots & a_{n,n} \end{pmatrix}\times (-1) \]

行列式加法,

\[A_{2,1}=\det\begin{pmatrix} -a_{2,2} & -a_{2,3} & \cdots & -a_{2,n} \\ a_{3,2} & a_{3,3} & \cdots & a_{3,n} \\ \vdots & \vdots & \ddots &\vdots \\ a_{n,2} & a_{n,3} & \cdots & a_{n,n} \end{pmatrix}\times (-1)= \det\begin{pmatrix} a_{2,2} & a_{2,3} & \cdots & a_{2,n} \\ a_{3,2} & a_{3,3} & \cdots & a_{3,n} \\ \vdots & \vdots & \ddots &\vdots \\ a_{n,2} & a_{n,3} & \cdots & a_{n,n} \end{pmatrix}= A_{1,1} \]

即证。


因此删掉每一行都是等价的(除了一个 \(-1\) 的系数)。

思路到这儿就结束了。注意 \(A\)\(M\) 是有一个系数的。

现在开始研究写法:

我每一次删掉不同的列,还是要做一个 \(\mathcal{O}(n^3)\) 的行列式啊?

发现将 \(L\) 最后一行删掉的 \((n-1)\times n\) 矩阵做高斯消元以后,再删掉列求行列式是没有影响的(记录一个交换次数即可)。这个是因为高斯消元中我们没有做列交换,因此删掉一个列以后再做高斯消元是等价的(一个列只能影响这一个列)。

因此先做一个高斯消元,再删掉一列。现在得到的 \((n-1)\times (n-1)\) 的矩阵恰好是上海森堡矩阵。这个求行列式可以 \(\mathcal{O}(n^2)\) 求。好像有多种做法,但是简单的就是,发现一列最多只有两个位置(还是相邻的)有值,套高斯消元的外壳即可。当然也有一个展开行列式定义的 dp 做法,这里不再赘述。

因此我们 \(\mathcal{O}(n^3)\) 的完成了这个题。有可能写的时候会写到逆元,不过多一个 \(\log\) 也可以过。

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 5e2+2;
const ll mod = 1e9+7;

ll pw(ll x,ll y){
	ll res=1;
	while (y){
		if (y&1){
			res=res*x%mod;
		}
		x=x*x%mod;
		y>>=1;
	}
	return res;
}
ll inv(ll x){
	return pw(x,mod-2);
}

struct mat {
	int n,m,rank,swp;
	ll a[N][N];
	void init(int _n,int _m){
		n=_n,m=_m;
		memset(a,0,sizeof a);
		rank=0;
	}
	void guass(){
		for (int i=1; i<=n; i++){
			int p=i;
			for (int j=i+1; j<=m; j++){
				if (a[p][i]<a[j][i]){
					p=j;
				}
			}
			for (int j=1; j<=m; j++){
				swap(a[p][j],a[i][j]);
			}
			if (p!=i) swp++;
			for (int j=1; j<=n; j++){
				if (i!=j){
					ll d=a[j][i]*inv(a[i][i])%mod;
					for (int k=i; k<=m; k++){
						a[j][k]-=a[i][k]*d%mod;
						a[j][k]=(a[j][k]+mod)%mod;
					}
				}
			}
		}
		for (int i=1; i<=n; i++){
			if (a[i][i]) rank++;
		}
	}
	mat delj(int x){
		mat c;
		c.init(n,m-1);
		for (int i=1; i<=n; i++){
			for (int j=1; j<=m; j++){
				if (j<x) c.a[i][j]=a[i][j];
				else if (j!=x) c.a[i][j-1]=a[i][j];
			}
		}
		return c;
	}
	ll det(){
		// Hessenberg
		ll ans=1;
		for (int i=1; i+1<=n; i++){
			if (a[i+1][i]==0) continue;
			if (a[i][i]==0){
				swap(a[i],a[i+1]);
				ans*=-1;
			}
			ll d=a[i+1][i]*inv(a[i][i])%mod;
			for (int j=i; j<=n; j++){
				a[i+1][j]=(a[i+1][j]-d*a[i][j]%mod)%mod;
			}
		}
		if (swp&1) ans*=-1;
		for (int i=1; i<=n; i++) ans=ans*a[i][i]%mod;
		return (ans+mod)%mod;
	}
} lap,lar,d;

int n,m;

void solve(){
	cin>>n>>m;
	lap.init(n,n);
	for (int i=1; i<=m; i++){
		int u,v;
		cin>>u>>v;
		lap.a[v][v]++;
		lap.a[u][v]--;
	}
	lar=lap;
	lap.guass();
	if (lap.rank<n-1){
		for (int i=0; i<n; i++){
			cout<<0<<" ";
		}
		cout<<"\n";
		return;
	}
	// rank=n-1
	lar.swp=0;
	lar.n--;
	lar.guass();
	for (int i=1; i<=n; i++){
		d=lar.delj(i);
		d.swp=lar.swp;
		int ans=d.det();
		cout<<((i+n)%2?(mod-ans)%mod:ans)<<" ";
	}
	cout<<"\n";
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	int t;
	cin>>t;
	while (t--){
		solve();
	} 
	return 0;
}
posted @ 2025-08-26 09:58  SFlyer  阅读(24)  评论(0)    收藏  举报