题目链接
https://ac.nowcoder.com/acm/contest/11260/G
题目大意
有一棵
n
n
n 个节点的树,根为
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 的概率是可存储节点。如果球滚到可储存可存储节点就会立刻破裂,然后从树上取下。如果球初始就在可存储节点上,会立刻破裂。
两个球如果同时滚到同一个节点就会发生碰撞,不论节点是否为可存储节点。如果发生碰撞,不仅两个球会碎,整个系统也会崩坏,所有滚动都会停止。
如果发生碰撞,则分数为
∑
i
=
1
n
f
(
i
)
\sum_{i=1}^nf(i)
∑i=1nf(i),其中
f
(
u
)
f(u)
f(u) 表示初始在
u
u
u 上的球所滚过的边数。
求分数的期望。
题解
由题意得,两个球在同一个节点出现就会崩坏,所以一个节点只能有一个或零个球经过,所以该节点不崩坏的概率为一个球
s
i
z
e
i
∗
(
1
−
p
)
p
s
i
z
e
i
−
1
size_i*(1-p)p^{size_i-1}
sizei∗(1−p)psizei−1加上零个
p
s
i
z
e
i
p^{size_i}
psizei
(
s
i
z
e
i
(size_i
(sizei表示第
i
i
i个节点的子节点数
)
)
)。
所以整棵树的合法概率是所有节点相乘,即
P
=
∏
i
n
s
i
z
e
i
∗
(
1
−
p
)
p
s
i
z
e
i
−
1
+
p
s
i
z
e
i
P=\prod_{i}^{n}size_i*(1-p)p^{size_i-1}+p^{size_i}
P=∏insizei∗(1−p)psizei−1+psizei。
我们设
d
p
i
dp_i
dpi为合法的
i
i
i 节点上球的期望,从根节点出发求每个节点的
d
p
dp
dp值。
(不是吧不是吧,有人期望不是倒着求的?)
已知该节点所有合法的情况为
s
i
z
e
i
∗
(
1
−
p
)
p
s
i
z
e
i
−
1
+
p
s
i
z
e
i
size_i*(1-p)p^{size_i-1}+p^{size_i}
sizei∗(1−p)psizei−1+psizei(设
j
j
j为
i
i
i的父节点)
能到
i
i
i的合法情况为
(
1
−
p
)
∗
p
s
i
z
e
j
−
1
(1-p)*p^{size_j-1}
(1−p)∗psizej−1
同时从父节点能遗传到得它的信息。
所以总结可以得到
d
p
i
=
(
1
+
d
p
j
)
(
1
−
p
)
∗
p
s
i
z
e
j
−
1
s
i
z
e
i
∗
(
1
−
p
)
p
s
i
z
e
i
−
1
+
p
s
i
z
e
i
dp_i=(1+dp_j)\frac{(1-p)*p^{size_j-1}}{size_i*(1-p)p^{size_i-1}+p^{size_i}}
dpi=(1+dpj)sizei∗(1−p)psizei−1+psizei(1−p)∗psizej−1
最后将得到的
d
p
dp
dp综合乘上合法总数
P
P
P。
过程中需要运用逆元,快速幂,注意时刻取模。
参考代码
#include<bits/stdc++.h>
#define pb push_back
#define FOR(i,n,m) for(int i=n;i<=m;i++)
using namespace std;
const int N=5e5+5;
const int mod=998244353;
int n,p;
int dp[N],siz[N],f[N];
long long s=0,P=1;
vector<int> v[N];
void read(int &x) //快读
{
int ret=0;
char c=getchar();
while(!isdigit(c))
c=getchar();
while(isdigit(c))
ret=ret*10+c-'0',c=getchar();
x=ret;
}
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;
}
void dfs(int x) //求所有节点合法概率P
{
if(v[x].size()==0)
return;
FOR(i,0,v[x].size()-1)
dfs(v[x][i]);
P=1ll*P*((1ll*siz[x]*(1-p+mod)%mod*powmod(p,siz[x]-1)%mod+powmod(p,siz[x]))%mod)%mod;
}
void sol(int x) //求每个节点的期望dp[x]
{
dp[x]=1ll*(1+dp[f[x]])*(1-p+mod)%mod*powmod(p,siz[f[x]]-1)%mod*powmod(1ll*siz[f[x]]*(1-p+mod)%mod*powmod(p,siz[f[x]]-1)%mod+powmod(p,siz[f[x]]),mod-2)%mod; //除法转逆元
if(v[x].size()==0)
return;
FOR(i,0,v[x].size()-1)
sol(v[x][i]);
}
int main()
{
read(n);
read(p);
if(n==1)
{
puts("0");
return 0;
}
FOR(i,2,n)
{
read(f[i]); //读入父亲
v[f[i]].push_back(i); //构边
}
FOR(i,1,n)
siz[i]=v[i].size();
dfs(1);
FOR(i,0,v[1].size()-1)
sol(v[1][i]);
FOR(i,1,n)
s=(s+dp[i])%mod; //累加
printf("%lld",s*P%mod);
}