[JZOJ5899]【NOIP2018模拟10.6】资源运输【矩阵树定理】【图论】

版权声明:本文为博主原创文章,未经博主允许不得转载。 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改成边权,一样做生成树计数就是答案…
证明同矩阵树定理的证明是类似的。

总时间复杂度 O ( n 3 + m ) O(n^3+m)
不会生成树计数的同学参考:

不明白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);
}

猜你喜欢

转载自blog.csdn.net/hzj1054689699/article/details/83060060