首先不難發現整個圖構成的結構是一棵樹,若是這個東西是一個外向樹的話,那麼咱們在乎的只有這棵子樹內的順序關係,子樹外的關係與這棵子樹之間的限制無關。因此咱們只須要強制根節點在其餘兒子以前的就好了(你能夠認爲若是此次隨機抽到了子樹外面的東西就從新抽一次,這個機率等於只考慮子樹權值和的機率),那麼這裏的機率就是\(\frac{w_u}{\sum w}\)。而後每一個根節點顯然能夠獨立考慮,因此只須要把全部根節點的結果直接乘起來就行了。
那麼對於\(w\)也有機率的狀況,設\(f[i][w]\)表示以\(i\)爲根的子樹中,權值和爲\(w\)時根節點合法的機率。
這個隨便轉移一下就很好作了。
如今加上了反向邊,反向邊強制了兒子要在根節點以前出現,而狀態也只要兩種,要麼反向邊在前要麼反向邊在後,那麼設\(f[i][w][j]\)表示以\(i\)爲子樹,子樹和爲\(w\),至少有\(j\)條反向邊不知足條件的機率,既然強制了若干個不反向,那麼就是你枚舉一些邊,而後強制把它變成正向邊,剩下的反向邊直接刪掉,這樣子就能夠求出這個機率。
注意到這個容斥的係數就是簡單的\(\pm 1\),因此只須要直接把容斥係數帶進去算就好了。
這樣子複雜度能夠作到\(O(n^2)\)。spa
#include<iostream> #include<cstdio> using namespace std; #define MOD 998244353 #define MAX 1010 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int fpow(int a,int b){int s=1;while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}return s;} struct Line{int v,next;}e[MAX<<1]; int h[MAX],cnt=1; inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;} int f[MAX][3*MAX],sz[MAX],p[MAX][4],inv[MAX*3],tmp[MAX*3],n,ans; void dfs(int u,int ff) { sz[u]=1; for(int i=h[u];i;i=e[i].next) { int v=e[i].v;if(v==ff)continue; dfs(v,u); for(int j=0;j<=3*sz[u];++j) for(int k=0;k<=3*sz[v];++k) { int val=1ll*f[u][j]*f[v][k]%MOD; if(i&1)tmp[j+k]=(tmp[j+k]+val)%MOD; else tmp[j+k]=(tmp[j+k]+MOD-val)%MOD,tmp[j]=(tmp[j]+val)%MOD; } sz[u]+=sz[v];for(int j=0;j<=3*sz[u];++j)f[u][j]=tmp[j],tmp[j]=0; } for(int j=0;j<=sz[u]*3;++j)f[u][j]=1ll*f[u][j]*inv[j]%MOD; } int main() { n=read(); for(int i=1;i<=n;++i) { int a1=read(),a2=read(),a3=read(); int inv=fpow(a1+a2+a3,MOD-2); f[i][1]=1ll*a1*inv%MOD; f[i][2]=2ll*a2*inv%MOD; f[i][3]=3ll*a3*inv%MOD; } for(int i=1;i<n;++i) { int u=read(),v=read(); Add(u,v);Add(v,u); } inv[0]=inv[1]=1;for(int i=2;i<=3*n;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD; dfs(1,0);for(int i=0;i<=3*n;++i)ans=(ans+f[1][i])%MOD; printf("%d\n",ans); return 0; }