氪金手游

题目传送门。

不妨假设一开始就确定了所有的 \(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;
}
posted @ 2026-01-18 17:01  Oken喵~  阅读(2)  评论(0)    收藏  举报