[模板] 矩阵树定理

简介

矩阵树定理 - OI Wiki

矩阵树定理可以求无/有向图生成树个数.

对于无向图:
定义度数矩阵 (D), 邻接矩阵 (E), 那么基尔霍夫矩阵 (K = D - E).

图的生成树个数 (t(G) = det(K_{ii})), 其中 (K_{ii}) 表示(K) 去掉第 (i) 行和第 (i) 列的余子式.

不会证...

矩阵树定理也适用于有向图:

根向树形图 (leftrightarrow K_{out});

叶向树形图 (leftrightarrow K_{in}).

总之不是根就是啦...

推论

  1. (n) 个点有标号完全图生成树个数为 (n^{n-2}).
  2. 一个完全二分图, 两部分点数分别为 (n_1), (n_2), 它的生成树个数为 $n_1^{n_2 - 1} n_2^{n_1 - 1} $.

利用 prüfer 序列可以得到相同的结果.

另外, 使用 prüfer 序列还可以得到如下结果:

  1. (n) 个点, 每个点 (i) 度数为 (d_i), 树的个数为

    [inom {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);
}

原文地址:https://www.cnblogs.com/ubospica/p/10439662.html