生成树个数(基尔霍夫矩阵)
来源:互联网 发布: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));}
- 生成树个数(基尔霍夫矩阵)
- hdu4305Lightning 生成树计数(基尔霍夫矩阵)+高斯消元+逆元
- 运用基尔霍夫矩阵树定理计算图的生成树个数
- 生成树的计数与基尔霍夫矩阵
- 【hdu5304】生成树计数—基尔霍夫矩阵 DP
- 【BZOJ 1016】[JSOI2008]最小生成树计数 基尔霍夫矩阵||暴力
- 基尔霍夫矩阵
- 生成生成树计数 --- Matrix-Tree定理(基尔霍夫矩阵树定理)
- hdu 5304 基尔霍夫矩阵
- BZOJ 1002: [FJOI2007]轮状病毒 递推/基尔霍夫矩阵树定理
- BZOJ 题目1002 [FJOI2007]轮状病毒(高精度+基尔霍夫矩阵)
- bzoj 1002: [FJOI2007]轮状病毒(打表找规律或基尔霍夫矩阵)
- 求生成树的个数(矩阵+乘法逆元)
- BZOJ 1002([FJOI2007]轮状病毒-基尔霍夫矩阵)
- 基尔霍夫矩阵计算欧拉回路
- 多小第一场:hdu:4305(矩阵求生成树的个数+乘法逆元)
- bzoj 1002 [FJOI2007]轮状病毒 找规律/基尔霍夫矩阵
- 【BZOJ 4031】[HEOI2015]小Z的房间 基尔霍夫矩阵
- 使用TCP协议和多线程实现实时聊天室
- 【暑期多校联萌
- 作用域与闭包
- PowerDesigner的报表功能导出表的清单
- python进阶(8):常用模块2+异常处理
- 生成树个数(基尔霍夫矩阵)
- [编程题] 彩色的砖块
- 17、坐标移动(string转char[])
- Stacking Plates UVA
- 用telnet测试给163邮箱发邮件
- 使用intent在活动之间穿梭——使用显式Intent
- ROBOCON——TITR——2015回顾
- 关于Handler进行倒计时服务
- Checking an Alibi