[CTS2019]氪金手游 解题报告
有 \(n\) 种卡牌,第 \(i\) 种卡牌有一个隐形权值 \(w_i\) ,每一次第 \(i\) 种卡牌被抽到的概率是 \(\frac{w_i}{\sum\limits_{i=1}^n w_i}\) ,第 \(i\) 种卡牌的隐形权值分别有概率是 \(1,2,3\) 中的一个,这些概率输入中会给出。
给出 \(n-1\) 个二元组 \((x,y)\) ,满足这是一个树形结构,要求满足 \(T_x\le T_y\) ,其中 \(T_x\) 是第 \(x\) 种卡牌被抽到的时间。
\(n\le 1000\)
比较神仙的树上概率题,我一开始搞错了方向,想着先搞出每种卡牌被抽到的概率,还以为第 \(i\) 种卡牌被抽到的概率是这个卡牌的隐形权值的期望值除以所有卡牌的隐形权值的期望值之和,被教育了,某嘴巴怪还感性证伪了这个东西,至于如何求,那就是另外一个故事了。
而且求出来也不能用到这个题里,因为是隐形权值,不是每次选都会打乱,所以要考虑枚举权值和,搞个背包。
先考虑外向树的情况,此时对于一个节点 \(u\) ,要求节点 \(u\) 要比子树内的所有节点都先取到,设 \(w_u\) 为节点 \(u\) 的权值, \(sw_u\) 为子树的权值之和, \(sum\) 为所有节点的权值之和,那么满足条件的概率为 \(\frac{w_u}{sum}\sum\limits_{i=0}^{\infty} (\frac{sum-sw_u}{sum})=\frac{w_u}{sum}\times \frac{sum}{sw_u}=\frac{w_u}{sw_u}\) 。
这个概率只与子树有关,可以做树形背包,设 \(dp_{u,i}\) 为在 \(u\) 的子树内,权值和为 \(i\) 的满足条件的概率,假如所有儿子的背包合并后是 \(dp^{'}_{i}\) ,那么有 \(dp_{u,i}=\sum\limits_{j=1}^3 p_{u,j}\times dp^{'}_{i-j}\times \frac{j}{i}\) ,为了方便可以将除以权值和放在最后,前面的先进行背包的合并,具体看代码。
如果存在反向边,有一个阳间想法就是做容斥,最后答案就是至少不满足偶数条反向边减去至少不满足奇数条反向边。
如果要至少不满足 \(x\) 条反向边,那么就找 \(x\) 条反向边变为正向边,其余的反向边是否满足无所谓,可以断掉,这就变成了一个外向边森林。
最暴力的做法是枚举有哪些反向边做状压,但是转移时不用那么多信息,可以变为枚举不满足多少条反向边。
注意最后答案是至少不满足偶数条反向边减去至少不满足奇数条反向边,对于 \(u\) 的一个儿子 \(v\) ,如果 \(u\) 到 \(v\) 是反向边,将这个反向边加入会导致所有条数+1,也就是对于 \(v\) 的 dp 乘上 -1 ,如果不加入,那么就可以断掉这条边,对于 \(v\) 的 dp 对对于 \(u\) 的总权值没有影响,权值不变。
注意加入容斥后, \(dp_{u,i}\) 表示的是以 \(u\) 为根的外向树的权值,与其不连通的不算在内,我之前没有理解到位被搞的晕乎乎orz
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int M=1005,JYY=998244353;
int n,Ans=0,a[M][4];
int read(){
int x=0,y=1;char ch=getchar();
while(ch<'0'||ch>'9') y=(ch=='-'?-1:1),ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*y;
}
int tot=0,first[M];
struct Edge{ int nxt,to,typ; }e[M<<1];
void add(int x,int y,int z){
e[++tot]=(Edge){first[x],y,z};
first[x]=tot;
}
int qpow(int x,int y){
int res=1;
for(;y;y>>=1,x=x*x%JYY) if(y&1) res=res*x%JYY;
return res;
}
int inv(int x){ return qpow(x,JYY-2); }
int sz[M],fz[M*3],dp[M][M*3];
void dfs(int u,int fa){
sz[u]=1;
for(int i=1;i<=3;i++) dp[u][i]=a[u][i]*i%JYY;
for(int i=first[u];i;i=e[i].nxt){
int v=e[i].to;if(v==fa) continue ;
dfs(v,u);
for(int j=1;j<=sz[u]*3;j++){
for(int k=1;k<=sz[v]*3;k++){
int w=dp[u][j]*dp[v][k]%JYY;
if(!e[i].typ) fz[j+k]=(fz[j+k]-w+JYY)%JYY,fz[j]=(fz[j]+w)%JYY;
else fz[j+k]=(fz[j+k]+w)%JYY;
}
}
sz[u]+=sz[v];
for(int j=1;j<=sz[u]*3;j++) dp[u][j]=fz[j],fz[j]=0;
}
for(int i=1;i<=sz[u]*3;i++) dp[u][i]=dp[u][i]*inv(i)%JYY;
}
void solve(){
n=read();
for(int i=1;i<=n;i++){
int sum=0;
for(int j=1;j<=3;j++) sum=(sum+(a[i][j]=read()))%JYY;
sum=inv(sum);
for(int j=1;j<=3;j++) a[i][j]=a[i][j]*sum%JYY;
}
for(int i=2;i<=n;i++){
int x=read(),y=read();
add(x,y,1),add(y,x,0);
}
dfs(1,0);
for(int i=1;i<=sz[1]*3;i++) Ans=(Ans+dp[1][i])%JYY;
printf("%lld\n",Ans);
}
signed main(){
// freopen("shuju.in","r",stdin);
solve();
}