这里简单讲一下矩阵树定理:
我们分 3 种情况:
1. 给定一个无向图,求生成树个数.
2. 给定一个有向图,求以一个点为根的内向树个数.
3. 给定一个有向图,求以一个点为根的外向树个数.
case1:
构造度数矩阵 $S1$,满足 $S1_{i,i}$ 等于 $i$ 点的度数(一条无向边当成两条有向边,且只有对角线有值)
构造邻接矩阵 $S2$,满足 $S2_{i,j}$ 等于 $(i,j)$ 之间边的个数(一条无向边当成两条有向边)
令矩阵 $d=S1-S2$,则生成树个数就等于 $d$ 去掉任意一行+一列后的行列式的值.
case2:
将 $1$ 中那个 $S1$ 矩阵中 $S1_{i,i}$ 变成 $i$ 点的出度.
然后去掉矩阵 $d$ 中根节点所在的行与列.
case3:
将 $1$ 中那个 $S1$ 矩阵中 $S1_{i,i}$ 变成 $i$ 点的入度.
然后去掉矩阵 $d$ 中根节点所在的行与列.
code:
#include <cstdio> #include <algorithm> #define N 300 #define mod 10007 #define ll long long #define setIO(s) freopen(s".in","r",stdin) using namespace std; int a[N][N],n,m; int qpow(int x,int y) { int tmp=1; for(;y;y>>=1,x=(ll)x*x%mod) if(y&1) tmp=(ll)tmp*x%mod; return tmp; } int INV(int x) { return qpow(x,mod-2); } int gauss() { int ans=1,i,j,k; for(i=2;i<=n;++i) { k=i; for(j=i+1;j<=n;++j) if(a[j][i]>a[k][i]) k=j; if(k!=i) swap(a[i],a[k]),ans*=-1; if(!a[i][i]) return 0; int inv=INV(a[i][i]); for(j=i+1;j<=n;++j) { int t=(ll)(inv*a[j][i]%mod+mod)%mod; for(k=i;k<=n;++k) a[j][k]=(ll)(a[j][k]%mod-(ll)t*a[i][k]%mod+mod)%mod; } } if(ans<0) ans+=mod; for(i=2;i<=n;++i) ans=(ll)(ans*a[i][i]%mod+mod)%mod; return ans; } int main() { // setIO("input"); int i,j; scanf("%d%d",&n,&m); for(i=1;i<=m;++i) { int x,y; scanf("%d%d",&x,&y); // y->x --a[y][x]; ++a[x][x]; } printf("%d ",gauss()); return 0; }