[模板] 矩阵树定理

简介

矩阵树定理 - 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\), 树的个数为

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

posted @ 2019-02-26 20:04  Ubospica  阅读(498)  评论(0编辑  收藏  举报