2021牛客多校#9 G-Glass Balls

原题链接

2021牛客多校第九场G题

题目大意

有一颗 n n n个节点的树,根为 1 1 1,每个节点上有 1 1 1个玻璃球。
对于 v v v的任意儿子 u u u u u u的高度比 v v v 1 1 1个单位,因此 u u u上的玻璃球可以沿 ( u , v ) (u,v) (u,v)边滚到 v v v。球沿一边滚动需要花费 1 1 1秒。

所有球同时开始滚动。有一些节点时刻储存节点,其中根节点必定是可储存节点,其他节点有 p p p的概率是可储存节点。如果球滚到可储存节点就会立刻破裂,然后从树上取下。如果球初始就在可存储节点上,会立刻破裂。

两个球如果同时滚到同一个节点上就会发生碰撞,不论节点是否是可存储节点,如果发生碰撞,不仅两个球会碎,整个系统也会崩坏,所有滚动都会停止。

如果发生碰撞,则分数为 0 0 0,否则分数为 ∑ i = 1 n f ( i ) \sum^{n}_{i=1}f(i) i=1nf(i),其中 f ( u ) f(u) f(u)表示初始在 u u u上的球所有滚过的边数。
求分数的期望。

题解

因为两个球不能同时滚到同一个节点上,所以就相当于某个节点的子节点中,至多有一个点不是存储节点(也可以一个存储节点也没有),才能保证不会有两个球在这个节点上发生碰撞,而每个节点为存储节点的概率为 p p p,假设 i i i节点有 s i z i siz_i sizi个子节点,那么 i i i点不会发生碰撞的概率为: s i z i × ( 1 − p ) × p s i z i − 1 ( siz_i\times (1-p)\times p^{siz_i-1}( sizi×(1p)×psizi1( 有一个存储子节点 ) + )+ )+ p s i z i ( p^{siz_i}( psizi(没有存储子节点 ) ) )

所以整颗树不发生碰撞的概率为 P P P,则 P P P为:
P = ∏ i s i z i × ( 1 − p ) × p s i z i − 1 + p s i z i P=\prod_{i}siz_i\times (1-p)\times p^{siz_i-1}+p^{siz_i} P=isizi×(1p)×psizi1+psizi

假设 d p x dp_x dpx表示节点 x x x上球的期望, y y y表示其父节点,如果节点 x x x上的球可以滚到 y y y,则此时 d p x = d p y + 1 dp_x=dp_y+1 dpx=dpy+1。但是此时可能为非法情况(该节点为存储节点),所以我们还需要考虑情况是否合法。
节点 x x x不是存储节点的概率为: ( 1 − p ) × p s i z y − 1 (1-p)\times p^{siz_y-1} (1p)×psizy1
同时还要保证 y y y节点中有一个子节点不是存储节点,其概率为: s i z y × ( 1 − p ) × p s i z y − 1 + p s i z y siz_y\times (1-p)\times p^{siz_y-1}+p^{siz_y} sizy×(1p)×psizy1+psizy

所以得到最终转移式:
d p x = ( d p y + 1 ) × ( 1 − p ) × p s i z y − 1 s i z y × ( 1 − p ) × p s i z y − 1 + p s i z y dp_x=(dp_y+1)\times \frac{(1-p)\times p^{siz_y-1}}{siz_y\times (1-p)\times p^{siz_y-1}+p^{siz_y}} dpx=(dpy+1)×sizy×(1p)×psizy1+psizy(1p)×psizy1

在最终计算答案时,需保证整棵树的合法性,所以答案为:
a n s = P × ∑ d p x ans=P\times \sum dp_x ans=P×dpx

参考代码

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define FOR(i,n,m) for(int i=n;i<=m;i++)
#define mod 998244353
using namespace std;
const int N=5e5+5;
int powmod(int a,int b)
{
	int ret=1;
	while(b)
	{
		if(b&1)ret=1ll*ret*a%mod;
		a=1ll*a*a%mod;
		b>>=1;
	}
	return ret;
}
int n,p,P=1;
int siz[N],dp[N],ans=0;
vector<int> e[N];
void dfs1(int x)
{
    int son;
    if(e[x].empty())return;
    FOR(i,0,e[x].size()-1)
    {
        son=e[x][i];
        dfs1(son);
    }
    P=1ll*P*((1ll*siz[x]*(1-p+mod)%mod*powmod(p,siz[x]-1)%mod+powmod(p,siz[x]))%mod)%mod;
}
//遍历每个节点并计算整棵树合法的概率P
void w(int x,int fa)
{
    int son;
    dp[x]=1ll*(1+dp[fa])*(1-p+mod)%mod*powmod(p,siz[fa]-1)%mod*powmod(1ll*siz[fa]*(1-p+mod)%mod*powmod(p,siz[fa]-1)%mod+powmod(p,siz[fa]),mod-2)%mod;
    if(e[x].empty())return;
	FOR(i,0,e[x].size()-1)
    {
        son=e[x][i];
        w(son,x);
    }
}
//计算每个节点的期望值dp[x]
int main()
{
    scanf("%d%d",&n,&p);
    if(n==1){puts("0");return 0;}
    FOR(u,2,n)
    {
        int v;
        scanf("%d",&v);
        e[v].pb(u);
    }
    FOR(i,1,n)siz[i]=e[i].size();//每个节点的子节点数
    dfs1(1);
    FOR(i,0,e[1].size()-1)w(e[1][i],1);
    FOR(i,1,n)ans=(1ll*ans+dp[i])%mod;
    printf("%d",1ll*P*ans%mod);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值