Infection
问题简述
一棵有n个节点的树,每个点有一定的概率被选为感染源,一个点有\(p[i]\)d的概率被旁边已感染的点传染,求最后整棵树中恰有k个点被感染的概率
分析
首先考虑这样一个问题,树中恰有一个大小为k的连通块被感染的概率是多少?其应满足所有点被感染的概率*相邻点未被感染的概率
考虑树上背包,因为感染源只有一个,所以当感染源在子树内部时,转移会变得十分困难,我们可以这样去设计DP的状态
- \(f[u][k]\) 以u为根的连通块中有k个点被感染,且其中无感染源的概率
- \(g[u][k]\) 以u为根的连通块中有k个点被感染,且其中有感染源的概率
首先分析\(f[u][k]\)的转移,由于其内部是没有感染源的,故转移满足简单的乘法原理,即:
\[f[u][i+j] = \sum_{son \in E(u)} f[u][i]*f[son][j]
\]
然后来分析\(g[u][k]\)的转移,我们可以将\(g[u][k]\)的转移分为两部分,即当前部分无感染源,要合并的部分有感染源和当前部分有感染源而要合并的部分无感染源
\[g[u][i+j] = \sum_{son \in E(u)} g[u][j]*f[son][j]+\sum_{son \in E(u)} f[u][i]*f[son][j]
\]
下面是代码实现:
#include<bits/stdc++.h>
#define ll long long
#define int long long
using namespace std;
const int maxm = 2005;
const int mod = 1e9+7;
int a[maxm],b[maxm],c[maxm];
int f[maxm][maxm],g[maxm][maxm];
int sz[maxm],Fa[maxm],tmp1[maxm],tmp2[maxm];
int p[maxm],sor[maxm],ans[maxm];//分别时被相邻点感染的概率和变成感染源的概率
vector<int>edge[maxm];
//f[u][k] 当前子树中无感染源,且被感染了k个
//g[u][k] 当前子树中有感染源,且被感染了k个
int quick_pow(int base,int p){
int ans = 1;
while(p){
if(p&1) ans = (ll)base*ans%mod;
base = (ll)base*base%mod;
p/=2;
}
return ans;
}
int inv(int x){
return quick_pow(x,mod-2);
}
void dfs(int now,int fa){
sz[now] = 1;Fa[now] = fa;
f[now][1] = p[now];f[now][0] = (1 - p[now]) + mod;
g[now][1] = sor[now];g[now][0] = (1 - p[now]) + mod;
for(auto u:edge[now]){
if(u==fa) continue;
dfs(u,now);
for(int i=0;i<=sz[now]+sz[u];i++) tmp1[i] = tmp2[i] = 0;
for(int j=1;j<=sz[now];j++){
for(int k=0;k<=sz[u];k++){
tmp1[j+k] = (tmp1[j+k] + (ll)f[now][j]*f[u][k]%mod)%mod;
tmp2[j+k] = (tmp2[j+k] + (ll)g[now][j]*f[u][k]%mod)%mod;
if(k) tmp2[j+k] = (tmp2[j+k] + (ll)f[now][j]*g[u][k]%mod)%mod;
}
}
sz[now] += sz[u];
for(int i=1;i<=sz[now];i++){
f[now][i] = tmp1[i];
g[now][i] = tmp2[i];
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int n,u,v;
cin>>n;
for(int i=1;i<n;i++){
cin>>u>>v;
edge[u].push_back(v);
edge[v].push_back(u);
}
int sum = 0;
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i]>>c[i];
sum = (sum+a[i])%mod;
}
for(int i=1;i<=n;i++){
sor[i] = (ll) a[i]*inv(sum)%mod;
p[i] = (ll)b[i]*inv(c[i])%mod;
}
dfs(1,0);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
ans[i] = (ans[i]+ (ll)(1-p[Fa[j]]+mod)%mod*g[j][i]%mod)%mod;
}
}
for(int i=1;i<=n;i++){
cout<<ans[i]<<'\n';
}
return 0;
}