1. 程式人生 > >[JZOJ5899]【NOIP2018模擬10.6】資源運輸【矩陣樹定理】【圖論】

[JZOJ5899]【NOIP2018模擬10.6】資源運輸【矩陣樹定理】【圖論】

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); }