[模板] 矩阵树定理
简介
矩阵树定理可以求无/有向图生成树个数.
对于无向图:
定义度数矩阵 \(D\), 邻接矩阵 \(E\), 那么基尔霍夫矩阵 \(K = D - E\).
图的生成树个数 \(t(G) = \det(K_{ii})\), 其中 \(K_{ii}\) 表示\(K\) 去掉第 \(i\) 行和第 \(i\) 列的余子式.
不会证...
矩阵树定理也适用于有向图:
根向树形图 \(\leftrightarrow K_{out}\);
叶向树形图 \(\leftrightarrow K_{in}\).
总之不是根就是啦...
推论
- \(n\) 个点有标号完全图生成树个数为 \(n^{n-2}\).
- 一个完全二分图, 两部分点数分别为 \(n_1\), \(n_2\), 它的生成树个数为 $n_1^{n_2 - 1} n_2^{n_1 - 1} $.
利用 prüfer 序列可以得到相同的结果.
另外, 使用 prüfer 序列还可以得到如下结果:
- \(n\) 个点, 每个点 \(i\) 度数为 \(d_i\), 树的个数为\[\binom {n-2}{d_1-1,d_2-1,\dotsc, d_n-1} \]
代码
//main part
//undirected
void adde(int f,int t){
++a[f][f],++a[t][t],--a[f][t],--a[t][f];
}
ll matt(){
return det(n-1);
}
//directed, to leaf
void adde(int f,int t){
++a[t][t],--a[f][t];
}
//to root
void adde(int f,int t){
++a[f][f],--a[f][t];
}
ll matt(int rt){
rep(i,1,n)if(i!=rt&&i!=n)swap(a[rt][i],a[n][i]),swap(a[i][rt],a[i][n]);
swap(a[rt][rt],a[n][n]);
return det(n-1);
}
//matrix
il ll getv(ll a){return a<0?a+nmod:a;}
il void exgcd(ll a,ll b,ll &x,ll &y,ll &d){//a&b should >= 0
b?(exgcd(b,a%b,y,x,d),y-=a/b*x):(x=1,y=0,d=x);
}
il ll inv(ll v){
ll x,y,d;
exgcd(getv(v),nmod,x,y,d);
return getv(x%nmod);
}
ll a[nsz][nsz];
ll det(int n){
ll ans=1;
ll v,tmp;
rep(i,1,n){
int p0=i;
rep(j,i+1,n)if(abs(a[p0][i])<abs(a[j][i]))p0=j;
if(a[p0][i]==0)return 0;
if(p0!=i){ans=-ans;rep(j,1,n)swap(a[i][j],a[p0][j]);}
v=inv(a[i][i]),ans=ans*a[i][i]%nmod;
rep(j,1,n){
if(j==i)continue;
tmp=v*a[j][i]%nmod;
rep(k,i,n)a[j][k]=(a[j][k]-tmp*a[i][k])%nmod;//possibly -
}
}
return getv(ans%nmod);
}
void adde(int f,int t){
++a[f][f],++a[t][t],--a[f][t],--a[t][f];
}
ll matt(){
return det(n-1);
}