题目描述
大意:给出n个点的有向图,求以1为根的树形图个数。(1≤n≤250)
思路
有向图的基尔霍夫定理。
类比无向图生成树的计算方式,先根据原图构出基佬♂霍夫矩阵。呸!是基尔霍夫矩阵。无向图中为度数矩阵-邻接矩阵,有向图为入度矩阵-邻接矩阵。(不证)
这题直接构建出矩阵,然后求出其余子式(即去掉某一行一列后的行列式,要求行列相同)。求行列式的基本想法就是通过初等行变换使之化成上三角,然后行列式就是主对角线的乘积。
初等行变换就用高消去搞,交换两行行列式取反,将某行乘K加到另一行行列式不变。取模就直接将除法改成逆元就行了。
顺便有一个性质:
拉普拉斯矩阵是半正定矩阵,半正定矩阵的行列式是非负的。——不是我说的
余子式Mij当i+j为偶数时非负。(如果是负数还怎么求其生成树个数啊)
于是在不用取模的情况下取绝对值就行了,可以不计交换次数。
注意这题要求根为1,所以只能求M(1,1)。
代码
#include <bits/stdc++.h>
#define maxn 255
#define M 10007
using namespace std;
int n, m, A[maxn][maxn];
int Pow(int x, int y){
int res = 1;
while(y){
if(y & 1) res = res * x % M;
x = x * x % M;
y >>= 1;
}
return res;
}
int Gauss(){
int ans = 1, cnt = 0;
for(int i = 2; i <= n; i++){
int x = i;
for(; x <= n && !A[x][i]; x++);
if(x != i) cnt ^= 1, swap(A[x], A[i]);
if(!A[i][i]) return 0;
ans = ans * A[i][i] % M;
int Inv = Pow(A[i][i], M-2);
for(int j = i+1; j <= n; j++){
if(!A[j][i]) continue;
int Mul = A[j][i] * Inv % M;
for(int k = 2; k <= n; k++)
A[j][k] = (A[j][k] - A[i][k] * Mul % M + M) % M;
}
}
if(cnt) ans = (M - ans) % M;
return ans;
}
int main(){
scanf("%d", &n);
scanf("%d", &m);
int a, b;
for(int i = 1; i <= m; i++){
scanf("%d%d", &a, &b);
A[b][a] --;
A[a][a] ++;
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
A[i][j] = (A[i][j] + M) % M;
printf("%d\n", Gauss());
return 0;
}