前言
证明不重要,结论直接背
普通矩阵树定理
一张无边权的无向图生成树个数为其基尔霍夫矩阵去掉某一行与相应列的行列式 (允许重边)
基尔霍夫矩阵: 度数矩阵(仅主对角线有值,值为该点度数的矩阵) - 邻接矩阵
有边权
用于统计无向图生成树的权值和 (生成树的权值为其边权乘积)
将边权作为同等数量的边加入基尔霍夫矩阵即可
有向边+指定根
外向树: 加正边入邻接矩阵,统计入度
内向边: 加反边入邻接矩阵,统计出度
指定根: 删除基尔霍夫矩阵中指定根节点对应的行和列即可
代码
struct matrix{
int a[_][_],n;
friend matrix operator -(matrix x,matrix y){ //矩阵减法
matrix c;
c.n=x.n;
for(re int i=1;i<=c.n;i++)
for(re int j=1;j<=c.n;j++) c.a[i][j]=(x.a[i][j]-y.a[i][j]+mod)%mod;
return c;
}
ll det()//行列式
{
ll ans=1, ff=1;
for(re int j=1;j<=n;++j)
{
for(re int i=j;i<=n;++i)
if(a[i][j]) {
if(i!=j){swap(a[i],a[j]); ff+=ff*(-2);}
break;
}
if(a[j][j]==0) return 0;
ans=ans*a[j][j]%mod;
ll inv=qpow(a[j][j],mod-2);
for(re int i=j;i<=n;i++) a[j][i]=a[j][i]*inv%mod;
for(re int i=j+1;i<=n;i++)
{
int t=a[i][j];
for(re int k=j;k<=n;++k)
a[i][k]=(a[i][k]-a[j][k]*t%mod+mod)%mod;
}
}
return ans*ff;
}
void SWAP(int k) //将第k行k列换到第n行n列(删除时直接n--,即可)
{
swap(a[k],a[n]);
for(re int i=1;i<=n;i++) swap(a[i][k],a[i][n]);
}
}A;
还有一种整数专用的辗转相除求行列式的办法
(用于处理模数为非质数时的询问)
struct matrix{
int n,a[_][_];
in ll det()
{
ll ans=1, ff=1;
for(re int i=1;i<=n;++i)
for(re int j=i+1;j<=n;++j)
{
while(a[i][i])
{
int div=a[j][i]/a[i][i];
for(re int k=i;k<=n;++k) a[j][k]=(a[j][k]-a[i][k]*div%mod+mod)%mod;
swap(a[j],a[i]); ff=-ff;
}
swap(a[j],a[i]); ff=-ff;
}
for(re int i=1;i<=n;++i) ans=ans*a[i][i]%mod;
return (ans*ff+mod)%mod;
}
}a;