氪金手游
不妨假设一开始就确定了所有的 \(W_u\),后续 dp 转移只要按照对应的概率取 \(W_u\) 即可。
观察 \((u_i,v_i)\) 的性质。容易发现其等价于这 \(n-1\) 个二元组构成一棵无向树的边。
首先考虑这棵树是外向树的情况,发现对于 \((u_i,v_i)\),\(u_i\) 必须要比 \(v_i\) 先抽到。
也就是说,对于 \(u\),其必须要是其子树内最先抽到的。
为了方便,令 \(S_u\) 表示 \(u\) 子树内 \(W_u\) 的和。
考虑第一次抽到 \(u\) 子树内节点的过程,\(u\) 从其中被抽到的概率为 \(\dfrac{W_u}{S_u}\)。
也就是说,\(u\) 在 \(u\) 子树内被最先抽到的概率为 \(\dfrac{W_u}{S_u}\),此时满足条件的概率就是 \(\prod\limits_{u=1}^{n} \dfrac{W_u}{S_u}\)。
不妨记 \(dp_{u,k}\) 表示 \(u\) 子树中满足 \(S_u = k\) 且合法的概率,这个可以树形背包转移。
接下来考虑有内向边的情况,此时我们的想法是进行容斥。
根据容斥原理,钦定一些内向边不满足,假设这样的边有 \(t\) 条,容斥系数系数为 \((-1)^t\)。
很难不注意到 \(u_i\) 和 \(v_i\) 必有其中一个比另一个先抽到,则非法的内向边等价于外向边。
对于没有钦定的内向边,这条边等于不存在,我们相当于断开了这条边。
dp 的时候正常转移,带上容斥系数即可,复杂度 \(O(n^2)\)。
//Ad astra per aspera
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const long long mod=998244353;
long long a[1010][4];
long long inv(long long num){
long long pre=mod-2,ans=1;
while(pre){
if(pre&1){
ans=ans*num%mod;
}
num=num*num%mod;
pre>>=1;
}
return ans;
}
struct Node{
int v,cmd;
};
vector<Node> G[1010];
long long dp[1010][3010],new_dp[3010],inv_num[3010];
int son[1010];
void dfs(int u,int fa){
dp[u][1]=a[u][1]*1%mod;
dp[u][2]=a[u][2]*2%mod;
dp[u][3]=a[u][3]*3%mod;
son[u]=1;
for(int i=0;i<G[u].size();i++){
int v=G[u][i].v,cmd=G[u][i].cmd;
if(v!=fa){
dfs(v,u);
if(cmd==0){
for(int cnt=0;cnt<=3*(son[u]+son[v]);cnt++){
new_dp[cnt]=0;
}
for(int cnt1=0;cnt1<=3*son[u];cnt1++){
for(int cnt2=0;cnt2<=3*son[v];cnt2++){
new_dp[cnt1+cnt2]+=dp[u][cnt1]*dp[v][cnt2]%mod;
new_dp[cnt1+cnt2]%=mod;
}
}
for(int cnt=0;cnt<=3*(son[u]+son[v]);cnt++){
dp[u][cnt]=new_dp[cnt];
}
}
else{
for(int cnt=0;cnt<=3*(son[u]+son[v]);cnt++){
new_dp[cnt]=0;
}
for(int cnt1=0;cnt1<=3*son[u];cnt1++){
for(int cnt2=0;cnt2<=3*son[v];cnt2++){
new_dp[cnt1+cnt2]-=dp[u][cnt1]*dp[v][cnt2]%mod;
new_dp[cnt1+cnt2]=(new_dp[cnt1+cnt2]%mod+mod)%mod;
new_dp[cnt1]+=dp[u][cnt1]*dp[v][cnt2]%mod;
new_dp[cnt1]%=mod;
}
}
for(int cnt=0;cnt<=3*(son[u]+son[v]);cnt++){
dp[u][cnt]=new_dp[cnt];
}
}
son[u]+=son[v];
}
}
for(int cnt=0;cnt<=3*son[u];cnt++){
dp[u][cnt]=dp[u][cnt]*inv_num[cnt]%mod;
}
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld %lld %lld",&a[i][1],&a[i][2],&a[i][3]);
long long num=inv(a[i][1]+a[i][2]+a[i][3]);
a[i][1]=a[i][1]*num%mod;
a[i][2]=a[i][2]*num%mod;
a[i][3]=a[i][3]*num%mod;
}
for(int i=1;i<n;i++){
int u,v;
scanf("%d %d",&u,&v);
G[u].push_back((Node){v,0});
G[v].push_back((Node){u,1});
}
for(int i=0;i<=3*n;i++){
inv_num[i]=inv(i);
}
dfs(1,-1);
long long ans=0;
for(int i=0;i<=3*n;i++){
ans+=dp[1][i];
ans%=mod;
}
printf("%lld",ans);
return 0;
}

浙公网安备 33010602011771号