首先不难发现整个图构成的结构是一棵树,若是这个东西是一个外向树的话,那么咱们在乎的只有这棵子树内的顺序关系,子树外的关系与这棵子树之间的限制无关。因此咱们只须要强制根节点在其余儿子以前的就好了(你能够认为若是此次随机抽到了子树外面的东西就从新抽一次,这个几率等于只考虑子树权值和的几率),那么这里的几率就是\(\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; }