版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hzj1054689699/article/details/83060060
Description
给定一个n个点,m条边的带权无向图。
定义这个图的一个生成树的权值为生成树上边权的乘积。
求所有生成树权值的平均值,答案对998244353取模。
2<=n<=300,n-1<=m<=1000
Solution
平均值=和/总数
总数很容易求,就是无向图生成树计数,利用矩阵树定理,求出这个图的基尔霍夫矩阵(就是度数矩阵(Ai,i=degree[i])-邻接矩阵),答案就是基尔霍夫矩阵挖掉第i行第i列(i可以任意取),用高斯消元求出挖掉以后矩阵行列式的值即可…
和的话,我们有变元矩阵树定理
把求基尔霍夫矩阵时的度数改成出边边权和,邻接矩阵0/1改成边权,一样做生成树计数就是答案…
证明同矩阵树定理的证明是类似的。
总时间复杂度
不会生成树计数的同学参考:
不明白NOIP模拟为什么要考生成树计数…为什么要出一道板题…
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 305
#define mo 998244353
#define LL long long
using namespace std;
int n,m;
LL a[2][N][N],t[2];
LL ksm(LL k,LL n)
{
LL s=1;
for(;n;n>>=1,k=k*k%mo) if(n&1) s=s*k%mo;
return s;
}
void gauss(int p)
{
fo(i,1,n-1)
{
fo(k,i,n-1)
{
if(a[p][k][i]>0)
{
if(k!=i) {t[p]=-t[p];swap(a[p][k],a[p][i]);}
break;
}
}
a[p][i][i]=(a[p][i][i]+mo)%mo;
LL v=ksm(a[p][i][i],mo-2);
fo(k,i+1,n-1)
{
LL v1=(a[p][k][i]*v%mo+mo)%mo;
if(v1!=0)
fo(j,i,n-1)
a[p][k][j]=(a[p][k][j]-v1*a[p][i][j]%mo+mo)%mo;
}
}
}
int main()
{
cin>>n>>m;
t[0]=t[1]=1;
fo(i,1,m)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
a[0][x][y]--,a[0][y][x]--,a[0][x][x]++,a[0][y][y]++;
(a[1][x][y]+=mo-z)%=mo,(a[1][y][x]+=mo-z)%=mo,(a[1][x][x]+=z)%=mo,(a[1][y][y]+=z)%=mo;
}
gauss(0);
gauss(1);
LL s1=1,s=1;
fo(i,1,n-1) s1=s1*a[0][i][i]%mo,s=s*a[1][i][i]%mo;
s=(s*t[0]+mo)%mo,s1=(s1*t[1]+mo)%mo;
printf("%lld\n",s*ksm(s1,mo-2)%mo);
}