SHOI2014 概率生成器
Problem Statement
你有 n n n个元件, 有 n − 1 n-1 n−1条导线将这些电子元件连成一张树状图. 每一个元件自身带电的概率为 q i q_i qi, 对于第 i t h i^{th} ith个导线 ( a i , b i ) (a_i,b_i) (ai,bi)它能导电的概率为 p i p_i pi. 问期望有多少个充电元件进入了充电状态.
Solution
由期望的线性性, 我们求出每一个元件进入充电状态的概率, 然后相加即可得到充电元件的期望个数.
那么一个充电元件带电有三种情况
- 自身带电
- 它父亲带电并且和父亲的导线是能导电的
- 它子节点至少有一个带电且导线是能导电的
这样我们就需要进行分类讨论了, 由于至少一个这个条件太强了, 需要进行容斥, 而一般容斥的复杂度是 2 n 2^n 2n级别的, 显然是不能够接受的. 我们考虑求元件 u u u不带电的概率假设为 P u P_u Pu. 那么节点 u u u带电的概率就是 1 − P u 1-P_u 1−Pu.
-
f u f_u fu表示 u u u这个节点不带电, 且子节点不能够将电传导到节点 u u u的概率.
-
g u g_u gu表示 u u u父节点不能够将电传到到节点 u u u的概率.
那么我们有 P u = f u × g u P_u=f_u\times g_u Pu=fu×gu, 因为一个节点不带电显然本身不能够带电并且不能从儿子或者父亲那边继承到电. 那么由期望的线性性答案就等于 A n s = ∑ i = 1 n ( 1 − f i × g i ) Ans=\sum_{i=1}^n(1-f_i\times g_i) Ans=∑i=1n(1−fi×gi).
我们先考虑自底向上计算 f u f_u fu.
首先 u u u节点是不能够带电的, 且子节点要么不带电, 要么带电但是导线不连通.
注意因为我们考虑 u u u是不带电的, 所以 u u u不可能影响到子节点是否带电.
于是我们有 f u = ( 1 − q u ) ∏ v ∈ son u [ f v + ( 1 − f v ) × ( 1 − p ( u , v ) ) ] f_u=(1-q_u)\prod\limits_{v\in\text{son}_u}[f_v+(1-f_v)\times(1-p_{(u,v)})] fu=(1−qu)v∈sonu∏[fv+(1−fv)×(1−p(u,v))].
其中 ( 1 − q i ) (1-q_i) (1−qi)是节点 u u u不带电的概率, f v f_v fv是子节点 v v v不带电的概率, ( 1 − f v ) × ( 1 − p ( u , v ) ) (1-f_v)\times(1-p_{(u,v)}) (1−fv)×(1−p(u,v))是节点 v v v带电但是导线不连通的概率.
接下来我们考虑自顶向下计算 g u g_u gu.
这里值得注意的是, 这过程类似于换根DP, 请使用换根DP的思想去理解 g u g_u gu的计算.
现在我们考虑节点 u u u, 由于 g u g_u gu是自顶向下计算的, 那么根节点一定有 g r o o t = 1 g_{root}=1 groot=1.
我们不妨设节点 u u u的父亲节点为 v v v.
我们设 t = g v × [ f v × 1 f u + ( 1 − f u ) × ( 1 − p ( u , v ) ) ] t=g_v\times [f_v\times\frac{1}{f_u+(1-f_u)\times(1-p_{(u,v)})}] t=gv×[fv×fu+(1−fu)×(1−p(u,v))1].
请关注中括号中的内容, f v × 1 f u + ( 1 − f u ) × ( 1 − p ( u , v ) ) = ( 1 − q v ) ∏ k ∈ son v 并 且 k ≠ u ( f k + ( 1 − f k ) × ( 1 − p ( k , v ) ) ] f_v\times\frac{1}{f_u+(1-f_u)\times(1-p_{(u,v)})}=(1-q_v)\prod\limits_{k\in\text{son}_v并且k\neq u}(f_k+(1-f_k)\times(1-p_{(k,v)})] fv×fu+(1−fu)×(1−p(u,v))1=(1−qv)k∈sonv并且k=u∏(fk+(1−fk)×(1−p(k,v))].
那么 t t t的实际含义就是, 我不妨把父节点 u u u拉下来变成节点 v v v的一个子树且这个子树不通电的概率,并统计相关的贡献.
那么 g u = t + ( 1 − t ) × ( 1 − p ( u , v ) ) g_u=t+(1-t)\times(1-p_{(u,v)}) gu=t+(1−t)×(1−p(u,v)). 也就是说这个本身就不通电或者通电但是道路不连通.
这里你可能会问, 为什么 g u g_u gu不用再次乘以一个 ( 1 − q u ) (1-q_u) (1−qu)呢, 这里要解释一下, 首先 ( 1 − q u ) (1-q_u) (1−qu)的贡献我们已经在 f u f_u fu中计算过了. 并且在自顶向下的过程中, 由于 t t t的计算中包含了 g v × f v g_v\times f_v gv×fv这意味着什么, 由于 f v f_v fv已经包含了 ( 1 − q v ) (1-q_v) (1−qv)所以已经隐含着 v v v节点不带电了, 所以上述等式都是成立的.
时间复杂度 O ( n ) O(n) O(n).
Code
# define Fast_IO std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
# include "unordered_map"
# include "algorithm"
# include "iostream"
# include "cstdlib"
# include "cstring"
# include "cstdio"
# include "vector"
# include "bitset"
# include "queue"
# include "cmath"
# include "map"
# include "set"
using namespace std;
const int maxm=5e5+10;
int N;
double Q[maxm],F[maxm],G[maxm];
vector<pair<int,double> > Edge[maxm];
void Dfs1(int Now,int Father){
F[Now]=1-Q[Now];
for(auto To:Edge[Now]){
if(To.first==Father) continue;
Dfs1(To.first,Now);
F[Now]*=(F[To.first]+(1-F[To.first])*(1-To.second));
}return;
}
void Dfs2(int Now,int Father){
for(auto To:Edge[Now]){
if(To.first==Father) continue;
double t;
if((F[To.first]+(1-F[To.first])*(1-To.second))!=0) t=F[Now]*G[Now]/(F[To.first]+(1-F[To.first])*(1-To.second));
else t=1;
G[To.first]=t+(1-t)*(1-To.second);
Dfs2(To.first,Now);
}return;
}
int main(){
static int i,u,v;
static double p,Ans; Ans=0;
scanf("%d",&N);
for(i=1;i<N;i++){
scanf("%d%d%lf",&u,&v,&p);
Edge[u].push_back({v,p/100.0});
Edge[v].push_back({u,p/100.0});
}
for(i=1;i<=N;i++) scanf("%lf",&Q[i]),Q[i]/=100.0;
Dfs1(1,1);
G[1]=1;
Dfs2(1,1);
for(i=1;i<=N;i++){
Ans+=(1-F[i]*G[i]);
}printf("%.6lf",Ans);
return 0;
}
Link
Link1: Luogu P4284 SHOI2014 概率充电器