G题:Glass Balls
题目大意
Solution
对一颗子树,子节点到父节点而不使系统崩溃的概率为
s
i
z
e
i
×
p
s
i
z
e
i
−
1
(
1
−
p
)
+
p
s
i
z
e
i
size_i\times p^{size_i-1}(1-p)+p^{size_i}
sizei×psizei−1(1−p)+psizei
其中sizei是父节点的儿子数量。
该式的来源有两部分
- 父节点的所有子节点都是可储存节点,概率 p s i z e i p^{size_i} psizei。
- 父节点的子节点中有一个不是可储存节点,其他所有点都是可储存节点,概率 s i z e i × p s i z e i − 1 ( 1 − p ) size_i\times p^{size_i-1}(1-p) sizei×psizei−1(1−p)。
设dpi表示i节点在不会发生崩溃的情况下期望走过的边数。
子节点若可以走到父节点,则可知
d
p
s
o
n
=
d
p
f
a
+
1
dp_{son}=dp_{fa}+1
dpson=dpfa+1
因为不知道是否崩溃,所以
d
p
s
o
n
dp_{son}
dpson的转移式要乘上概率系数(上文中第2种情况的概率系数)并除以总的不崩溃概率。
推得转移式
d
p
s
o
n
=
(
1
+
d
p
i
)
p
s
i
z
e
i
−
1
(
1
−
p
)
s
i
z
e
i
×
p
s
i
z
e
i
−
1
(
1
−
p
)
+
p
s
i
z
e
i
dp_{son}=\frac{(1+dp_{i})p^{size_i-1}(1-p)}{size_i\times p^{size_i-1}(1-p)+p^{size_i}}
dpson=sizei×psizei−1(1−p)+psizei(1+dpi)psizei−1(1−p)
整个系统不崩溃的概率是所有节点不崩溃的概率的乘积
P
=
Π
i
=
1
n
s
i
z
e
i
×
p
s
i
z
e
i
−
1
(
1
−
p
)
+
p
s
i
z
e
i
P=\Pi_{i=1}^n size_i\times p^{size_i-1}(1-p)+p^{size_i}
P=Πi=1nsizei×psizei−1(1−p)+psizei
最终分数的期望为整个系统不崩溃的概率,乘上所有节点不发生崩溃的情况下期望走过的边数的和。
A
n
s
=
P
⋅
∑
i
=
1
n
d
p
i
Ans=P\cdot \sum^n_{i=1}dp_i
Ans=P⋅i=1∑ndpi
注:使用逆元处理除法。
Code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=998244353;
const int N=5e5+7;
vector <int> ve[N];
int f[N],dp[N],g[N];//g[i]记录i为根节点的子树的所有节点不使系统崩溃的概率的乘积
ll ans;
int n,p;
ll fmx(ll x,ll y)//快速幂
{
ll ret=1;
while(y){
if(y&1) ret=ret*x%mod;
x=x*x%mod;
y/=2;
}
return ret;
}
void dfs(int x)//求整个系统不崩溃的概率
{
ll u,mul=1,cnt=0,son;
for(int i=0;i<ve[x].size();i++)
{
son=ve[x][i];
dfs(son);
cnt++;
mul=(mul*g[son])%mod;
}
if(cnt==0) g[x]=1;
u=(fmx(p,cnt)+fmx(p,cnt-1)*(1-p+mod)%mod*cnt%mod)%mod;
g[x]=(u*mul)%mod;
}
void dfs2(int x)//求dp
{
ll u,son;
ll size=ve[f[x]].size();
u=(fmx(p,size)+fmx(p,size-1)*(1-p+mod)%mod*size%mod)%mod;
dp[x]=(1+dp[f[x]])%mod*fmx(p,size-1)%mod*(1-p+mod)%mod*fmx(u,mod-2)%mod;
for(int i=0;i<ve[x].size();i++)
{
son=ve[x][i];
dfs2(son);
}
}
int main()
{
scanf("%d%d",&n,&p);
for(int i=2;i<=n;i++)
{
scanf("%d",&f[i]);
ve[f[i]].push_back(i);
}
dfs(1);
for(int i=0;i<ve[1].size();i++){
dfs2(ve[1][i]);
}
for(int i=1;i<=n;i++){//dp的总和
ans=(ans+dp[i]+mod)%mod;
}
printf("%lld\n",ans*g[1]%mod);//答案乘上整个系统不崩溃的概率
return 0;
}