[COCI 2023/2024 #3] Slučajna Cesta 题解
考虑如何
现在 Vito 从结点
对于一个结点
前文提到对于每个结点,往所有出边走的概率相等,可以借助这一点进行换根。即对于每个结点按比例加入从父亲下传的贡献即可。具体实现可以参考代码。
时间复杂度
放代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int p=1e9+7;
int qpow(int a,int b){
int r=1;
while(b){
if(b&1)(r*=a)%=p;
(a*=a)%=p,b>>=1;
}
return r;
}
int inv(int x){
return qpow(x,p-2);
}
int sp(int x){
return (1-qpow(inv(2),x)+p)*inv(x)%p;
} // 有 x 个儿子时走到单个儿子的概率
main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n; cin>>n;
vector<vector<int> > g(n);
for(int i=1;i<n;i++){
int f; cin>>f;
g[f-1].emplace_back(i);
}
vector<int> w(n),f1(n),f2(n),r(n);
for(auto &i:w)cin>>i;
function<void(int)> dfs1=[&](int u){
int x=sp(g[u].size());
for(int i:g[u])
dfs1(i),(f1[u]+=(f1[i]+w[i])*x%p)%=p;
}; // 处理出根结点答案
function<void(int,int)> dfs2=[&](int u,int f){
int x1=sp(g[u].size()),x2=sp(g[u].size()+1);
if(u){
int x3=sp(g[f].size());
if(f)f2[u]=((f2[f]-f1[u]-w[u]+(p<<1))*x3%p+f1[f]+w[f])%p; // 父亲不是根
else{
int x4=sp(g[f].size()-1);
f2[u]=((f1[f]-(f1[u]+w[u])*x3%p+p)*x4%p*inv(x3)%p+w[f])%p;
} // 父亲是根
r[u]=(f2[u]*x2%p+f1[u]*x2%p*inv(x1)%p+w[u])%p;
}
else r[u]=(f1[u]+w[u])%p; // 根结点
for(int i:g[u])dfs2(i,u);
};
dfs1(0),dfs2(0,0);
for(int i:r)cout<<i<<'\n';
return 0;
}