Description
在图论中,树的定义是连通且无环的无向图。对于一棵有 n 个节点且节点从 1 到 n 编号的树,它的 Prufer 序列是一个唯一的长为 n−2 的标号序列。 Prufer 序列的构造方法:每次删除树中标号最小的叶子节点(即度为 1 的节点),将该点的邻居加到当前 Prufer 序列的末尾,直到只剩两个节点为止。
例子:
给定一个 n 个顶点(从 1 到 n 标号), m 条边的无向图 G(G 中无重边或自环)。随机选择 G 的一棵生成树,计算他的 Prufer 序列的和 S,重复元素只算一次。 请计算随机变量 S 的期望。注意,G 的生成树或某棵生成树的 Prufer 序列都可能不存在,这种情况下,我们认为随机变量 S 的值为 0。
为了避免精度问题, 算出实际的期望值乘以图 G 的不同生成树的数目以后的值即可。 这个值可能很大,请输出它对 109+7 取模以后的值。
Input
每个输入文件包含多组测试数据。输入文件的第一行是测试数据组数 T (T≤10)。 对于每组测试数据,第一行是两个整数 n,m (3≤n≤100,0≤m≤(n−1)n2 ),分别是图的点数和边数;接下来 m 行,每行包含两个整数 u,v(1≤u,v≤n,u≠v),表示图中的一条边。
Output
输出 T 行,每行是对应的答案。
Sample Input
1
3 3
1 2
2 3
1 3
Sample Output
6
题目大意:求所有生成树的prufer序列和(prufer中有重复序列的只算一次!!!)
题解:
这样的话,对于每一颗生成树,我们可以把所有的点全都加进去,然后再减去叶子结点的和。
我们不可能找到所有的生成树然后一个一个的计算,因此我们用矩阵树定理来做。
我们先计算图所有的点的和,并且乘以生成树的数量,把他们放在sum里。然后再把所有的叶子结点减去,就好了
如果一个叶子节点出现在一颗子树里,那么把这个点去掉,仍然可一得到图的该生成树,而如果这个点是内部节点就不行了。
注意:如果这个叶子节点的度不为1,那么要用这个叶子节点的度数乘以生成树的数量,才是这个叶子节点对应生成树的个数。
sum -= 去掉该节点生成树的数量*该节点的度*该节点的值。
最后得到的sum就是答案
代码:
#include<iostream>。
#include<cmath>
#include <cstring>
using namespace std;
#define zero(x)((x>0? x:-x)<1e-15)
#define int long long
int const MAXN = 105;
const int mod = 1e9 + 7;
int a[MAXN][MAXN];
int b[MAXN][MAXN];
int g[103][103];
int d[105];
int n, m;
int det(int a[MAXN][MAXN], int n){
int s=0;
for(int i=0; i<n; i++){
int r=i;
for(; r<n; r++) // error-prone
if(a[r][i]) break;
if(r==n+1) return 0;
if(r!=i){
s^=1;
for(int j=i; j<n; j++)
swap(a[i][j], a[r][j]);
}
for(int j=i+1; j<n; j++){
int x=i, y=j;
for(; a[y][i]; ){
// print(a, n);
int t=a[y][i]/a[x][i];
if(t){
for(int k=i; k<n; k++){
a[y][k] -= t*a[x][k]%mod;
a[y][k] %= mod;
}
if(a[y][i]==0) break;
}
swap(x, y);
}
if(x!=i){
for(int k=i; k<n; ++k)
swap(a[x][k], a[y][k]);
s ^= 1;
}
}
}
int res=1;
for(int i=0; i<n; i++)
res*=a[i][i], res%=mod;
if(s)
{
res=-res;
}
if(res < 0) res+=mod;
return res;
}
void prep(int n,int x)
{
for(int i = 0;i < n;i++)
{
for(int j = 0;j < n;j++)
{
a[i][j] = (i == j)?d[i]-g[i][x]:-g[i][j];
}
}
if(x + 1)
{
for(int i = 0;i < n;i++) a[x][i] = a[i][x] = 0;
a[x][x] = 1;
}
}
main()
{
int cas;
scanf("%lld", &cas);
while (cas--) {
memset(g,0,sizeof(g));
memset(d,0,sizeof(d));
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
scanf("%lld%lld", &n,&m);
for(int i = 0;i < m;i++)
{
int u,v;
scanf("%lld%lld",&u,&v);
u--,v--;
d[u]++;
d[v]++;
g[u][v] = g[v][u] = 1;
}
prep(n,-1);
int sum = det(a,n-1)*((1+n)*n/2) % mod;
for(int i = 0;i < n-1;i++)
{
prep(n,i);
sum = (sum - (i+1)*d[i] % mod * det(a,n-1) % mod + mod)%mod;
//cout<<':'<<sum<<endl;
}
prep(n,n-1);
sum = (sum - n*d[n-1] % mod * det(a,n-2) % mod + mod)%mod;
cout<<sum<<endl;
}
return 0;
}