【樹】矩陣樹定理
矩陣樹定理
學習資料:OI Wiki
用途與時間複雜度
用於求圖(有向圖和無向圖)的生成樹個數計數問題。時間複雜度為 \(\mathcal{O(n^3)}\) ,其中 \(n\) 是圖的節點個數。需要掌握求矩陣行列式的方法。
定義與做法
無向圖:
定義度數矩陣 \(D(G)\) :
\[D_{i,i}(G)=deg(i),\;D_{i,j}(G)=0,\,i\ne j \]
設 \(\#e(i,j)\) 為點 \(i\) 與點 \(j\) 相連的邊數,定義鄰接矩陣 \(A(G)\) :
\[A_{ij}(G)=A_{ji}(G)=\#e(i,j),\,i\ne j \]
定義 Laplace 矩陣(亦稱 Kirchhoff 矩陣) \(L(G)\)
\[L(G)=D(G)-A(G) \]
圖 \(G\) 生成樹個數 \(t(G)\) :
\[t(G)=det\,L(G)\begin{pmatrix} 1,2,\dots,i-1,i+1,\dots n\\ 1,2,\dots,i-1,i+1,\dots n \end{pmatrix} \]
其中記號 \(L(G)\begin{pmatrix} 1,2,\dots,i-1,i+1,\dots n\\ 1,2,\dots,i-1,i+1,\dots n \end{pmatrix}\) 表示矩陣 \(L(G)\) 的第 \(1,\dots,i-1,i+1,\dots n\) 行與第 \(1,\dots,i-1,i+1,\dots n\)
即 \(t(G)\) 與 \(L(G)\) 少去任意一點構成的方陣的行列式的值相等。
設 \(\lambda_1,\lambda_2,\dots,\lambda_{n-1}\) 為 \(L(G)\) 的 \(n-1\) 個非零特徵值,那麼有:
\[t(G)=\frac1{n}\lambda_1\lambda_2\dots\lambda_{n-1} \]
有向圖:
定義度數矩陣 \(D^{out}(G)\) , \(D^{in}(G)\) :
\[D_{i,i}^{out}(G)=deg^{out}(i),\;D_{i,j}^{out}(G)=0,\,i\ne j \\ D_{i,i}^{in}(G)=deg^{in}(i),\;D_{i,j}^{in}(G)=0,\,i\ne j \]
設 \(\#e(i,j)\) 為點 \(i\) 指向點 \(j\) 的邊數,定義鄰接矩陣 \(A(G)\) :
\[A_{ij}(G)=A_{ji}(G)=\#e(i,j),\,i\ne j \]
定義 Laplace 矩陣(亦稱 Kirchhoff 矩陣) \(L^{out}(G)\) , \(L^{out}(G)\) 為:
\[L^{out}(G)=D^{out}(G)-A(G)\\ L^{in}(G)=D^{in}(G)-A(G) \]
設 \(t^{root}(G,k)\) 表示以 \(k\) 為根,且所有邊均指向父親的樹形圖個數。
設 \(t^{leaf}(G,k)\) 表示以 \(k\) 為根,且所有邊均指向子節點的樹形圖個數。
根向行樹形圖個數 \(t^{root}(G,k)\) :
\[t^{root}(G,k)=det\,L^{out}(G)\begin{pmatrix} 1,2,\dots,k-1,k+1,\dots n\\ 1,2,\dots,k-1,k+1,\dots n \end{pmatrix} \]
葉向形樹形圖個數 \(t^{in}(G,k)\) :
\[t^{leaf}(G,k)=det\,L^{in}(G)\begin{pmatrix} 1,2,\dots,k-1,k+1,\dots n\\ 1,2,\dots,k-1,k+1,\dots n \end{pmatrix} \]
BEST定理:
設 \(G\) 是有向尤拉圖,那麼 \(G\) 的不同歐拉回路總數 \(ec(G)\) 是:
\[ec(G)=t^{root}(G,k)\prod_{v\in V}(deg(v)-1)! \]
對尤拉圖 \(G\) 的任意兩個節點 \(k,k'\) ,都有 \(t^{root}(G,k)=t^{root}(G,k')\) ,且尤拉圖 \(G\) 的所有節點的入度和出度相等。
例題
無向圖生成樹計數模板題。
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define mkp(a,b) make_pair(a,b)
using namespace std;
typedef long long ll;
const int mod=1e9;
char s[15][15];
int id[110][110];
int D[110][110],A[110][110];ll L[110][110];
int n;
ll det(ll a[][110])
{
ll det=1,t;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
while(a[j][i]){
t=a[i][i]/a[j][i];
for(int k=i;k<=n;k++)
a[i][k]=(a[i][k]-t*a[j][k]%mod+mod)%mod;
swap(a[i],a[j]);
det*=-1;
}
}
if(!a[i][i])return 0;
else det=det*a[i][i]%mod;
}
return (det+mod)%mod;
}
int main()
{
int nn,mm,cnt=0,tid;
scanf("%d%d",&nn,&mm);
for(int i=1;i<=nn;i++)
scanf("%s",s[i]+1);
for(int i=1;i<=nn;i++)
for(int j=1;j<=mm;j++){
if(s[i][j]!='.')continue;
id[i][j]=++cnt;
if(s[i-1][j]=='.'){
tid=id[i-1][j];
D[cnt][cnt]++;D[tid][tid]++;
A[cnt][tid]++;A[tid][cnt]++;
}
if(s[i][j-1]=='.'){
tid=id[i][j-1];
D[cnt][cnt]++;D[tid][tid]++;
A[cnt][tid]++;A[tid][cnt]++;
}
}
for(int i=1;i<=cnt;i++) {
for(int j=1;j<=cnt;j++) {
L[i][j]=D[i][j]-A[i][j];
}
}
n=cnt-1;
// for(int i=1;i<=n;i++){
// for(int j=1;j<=n;j++)printf("%2lld ",L[i][j]);putchar(10);
// }
printf("%lld\n",det(L));
}
定根有向樹形圖計數模板題
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define mkp(a,b) make_pair(a,b)
using namespace std;
typedef long long ll;
const int mod=1e4+7;
const int N=330;
int L[N][N];
int n;
int kpow(int a,int b){
int ans=1;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
inline int ni(int a){return kpow(a,mod-2);}
int det(int A[][N])
{
int k;
int temp,det=1,nii;
for(int i=2;i<=n;i++)
{
k=i;
for(int j=i+1;j<=n;j++)
if(A[j][i])k=j;
if (k!=i)det*=-1;
swap(A[i],A[k]);
if(A[i][i]==0)return 0;
det=det*A[i][i]%mod;
temp=A[i][i];
nii=ni(temp);
for(int j=2;j<=n;j++)A[i][j]=A[i][j]*nii%mod;
for(int j=2;j<=n;j++){
if(j==i)continue;
temp=A[j][i];
for(int k=2;k<=n;k++)
A[j][k]=(A[j][k]-A[i][k]*temp%mod+mod)%mod;
}
}
return (det+mod)%mod;
}
int main()
{
int m,u,v;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
L[u][u]++;L[v][u]--;
}
printf("%d\n",det(L));
}