生成树个数(基尔霍夫矩阵)

来源:互联网 发布:sqlserver 字符串截取 编辑:程序博客网 时间:2024/06/05 10:03

Problem 2. tcount
Input file: tcount.in
Output file: tcount.out
Time limit: 1 second
Mr.H发现了一个无向连通图,它觉得,如果选出一些边来,使得这个图变为一棵树,那么这个边集就非常棒。
现在,Mr. H 想让你帮忙求出有多少个非常棒的边集?
Input
第1行,包含2个整数:n m,表示有n个点m条边。
接下来m行,每行2个整数:u v,表示一条边。
Output
输出1行,包含1个整数,表示方案数模10^9 + 7。
Sample
tcount.in
3 3
1 2
2 3
1 3
tcount.out
3
tcount.in
4 6
1 2
1 3
1 4
2 3
2 4
3 4
tcount.out
16
Note
对于30% 的数据,1 <= n;m <= 12;
对于100% 的数据,1 <= n <= 100, 1 <= m <= n(n-1)/2
数据保证没有重边。

思路:
度数矩阵 a[i][i]为点i的度 其他位置为0
邻接矩阵 a[i][i]=0; a[i][j]=[i与j相连]
基尔霍夫矩阵 = 度数矩阵 - 邻接矩阵
Matrix-Tree 定理:
一个n个点m条边的无向图的生成树总数为其对应的基尔霍夫矩阵的n-1阶余子式(行列式)。

行列式是什么?
一种定义是:
det(A) = Σ (-1)^(i1i2..in) * a1i1 * a2i2 anin
i1i2..in

其中i1i2…in 是一个1到n的排列,^(i1i2..in) 表示该排列的逆序对个数
行列式的性质
1.某行乘一个数,那么行列式也乘那个数。
2.某行加到另一行,行列式不变
3.交换两行,行列式取反相反数(交换两个数逆序对个数总是改变奇数个)
这里写图片描述
行列式的计算—高斯消元!
消元的过程中行列式不变,交换一次行列式*-1。所以消元完成后,矩阵只剩下对角线上的主元。
根据行列式的要求,我们要在矩阵中每一行找出且只能找出一个数 每一列找出且只能找出一个数。
那么就是对角线上的数了(只有一种排列),把对角线上的数乘起来就是行列式了。

由于中途有mod,double的除法又有可能炸精度。
所以有两种解决方法,一个是辗转相除,一个是逆元,下面的代码两种都给出来了。

#include <iostream>  #include <cstdio>  #include <cmath>  #define LL long longusing namespace std;  const int MOD = 1e9+7;LL a[105][105];int n, m, sign, du[105];void solve(int N){    sign = 0; LL ans = 1;    for(int i=1; i<N; i++){        for(int j=i+1; j<N; j++){//当前之后的每一行第一个数要转化成0            int x=i, y=j;            while( a[y][i] ){//利用gcd的方法,不停地进行辗转相除                LL t = a[x][i] / a[y][i];                for(int k=i; k<N; k++)                    a[x][k] = (a[x][k] - a[y][k] * t) % MOD;                swap(x, y);            }            if(x != i){//奇数次交换,则D=-D'整行交换                for(int k=1; k<N; k++)                    swap(a[i][k], a[x][k]);                sign ^= 1;//行列式*-1            }        }        if( !a[i][i] ){//斜对角中有一个0,则结果为0            printf("0\n");            return ;        }        else ans = ans * a[i][i] % MOD;    }    if(sign != 0) ans *= -1;    if(ans < 0) ans += MOD;//    printf("%I64d\n", ans);    return ;}int main(){    freopen ("tcount.in", "r", stdin);    freopen ("tcount.out", "w", stdout);    scanf("%d%d", &n, &m);    for(int i=1; i<=m; i++){        int x, y; scanf("%d%d", &x, &y);        a[x][y] = a[y][x] = -1;        du[x]++; du[y]++;    }    for(int i=1; i<=n; i++) a[i][i] = du[i];    solve(n);    return 0;}
#include <bits/stdc++.h>using namespace std;const int N = 110;const int Mod = 1e9 + 7;int n, m;int aa[N][N];int mpow(int a, int b) {    int rt;    for(rt = 1; b; b>>=1,a=(1LL*a*a)%Mod)        if(b & 1) rt=(1LL*rt*a)%Mod;    return rt;}int inverse(int a) {    return mpow(a, Mod-2);}int det(int n) {    int rt = 1;    for(int i=1; i<=n; i++) {        int j = -1, k;        for(k=i; k<=n; k++)            if(aa[k][i] != 0) {                j = k;                break;            }        if(j == -1) return 0;        if(i != j) {            for(k=i; k<=n; k++) {                swap(aa[i][k], aa[j][k]);                rt = (Mod - rt) % Mod;            }        }        int inv = inverse(aa[i][i]);        for(j = i + 1; j <= n; j++) {            if(aa[j][i] == 0) continue;            int r = 1LL * aa[j][i] * inv % Mod;            for(k = i; k <= n; k++) {                aa[j][k] -= 1LL * aa[i][k] * r % Mod;                if(aa[j][k] < 0) aa[j][k] += Mod;            }        }    }    for(int i = 1; i <= n; i++)        rt = 1LL * rt * aa[i][i] % Mod;    return rt;}int main() {    scanf("%d%d", &n, &m);    for(int i=1; i<=m; i++) {        int u, v;        scanf("%d%d", &u, &v);        aa[u][u]++;        aa[v][v]++;        aa[u][v] = aa[v][u] = Mod-1;    }    printf("%d\n", det(n-1));}
阅读全文
0 0