P4284 [SHOI2014]概率充电器
P4284 [SHOI2014]概率充电器
概率+树形DP
舒适查看
题目传送门
简化题目
给出一颗无根树,树上每一个点有概率p,对于边\(e_i=(u,v)\)存在\(p_i\)概率联通\(u,v\),求\(\sum_{i=1}^np_i\)
分析
不难想到,本题需要使用DP,怎么使用关键在于要知道DP转移的相关变量,我们先确定可能参加DP的变量,在一步一步缩圈,最后根据变量确定方程。
变量名 | 解释 |
---|---|
\(p_i\) | \(i点的初始概率\) |
\(P1_i\) | \(i点的son上传的概率\) |
\(P2_i\) | \(i点的集成概率\) |
\(siz_i\) | \(i点的子树大小\) |
\(v_i\) | \(i点的连边对应点\) |
\(w_i\) | \(i点的连边联通概率\) |
我们一个一个变量分析 |
- \(pi\)可能会参加方程,但是考虑到DP的状态集成思想,\(P1,P2\)更有可能参加方程。
- 对于这道题子树的大小以及类似(\(dep,fa\))等点特征变量很难关系到树形DP,所以暂时不考虑,如果题目需要特征会在后期突出,暂时不考虑。
- \(v,w\)明显会是转移对象
所以我们先初步确定一个范式:
\[P2_i=ans(P1_v,w_{i->v},P2_v,P2_i,P1_i)
\]
思考情况
1.自己来电\(p_i\)
2.子树上传\(P2_i\)
3.与父亲的关系\(g(P2_fa_i,P1_i)\)(g表示存在函数关系)
*3:来自父节点所以概率-父节点从i得来的概率
概率合并
\(p_a\)或\(p_b\)发生的概率=\(1-\)(\(p_a,p_b\)都不发生的概率)\(=1-(1-p_a)*(1-p_b)=p_a+p_b-p_a*p_b\)
于是可以愉快的转移了
对于第3种转移情况有一个方程可以手解,建议自己解一解,代码里也有。
完整代码
#include<bits/stdc++.h>
#define ll long long
#define db double
#define fd(i, a, b) for (ll i = a; i >= b; i--)
#define r(i, a) for (ll i = fir[a]; i; i = e[i].nex)
#define file(a) freopen(#a ".in", "r", stdin);
#define il inline
#define gc getchar()
#define f(i,a,b) for(ll i=a;i<=b;i++)
using namespace std;
const ll maxn=5e5+10,INF=1e16;
il ll read(){
ll x=0,f=1;char ch=gc;
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc;}
while(ch>='0'&&ch<='9') x=(x*10)+(ch^48),ch=gc;
return x*f;}
ll n,fir[maxn],cnt;
db p[maxn];
struct edge{ll to,nex;db w;}e[maxn<<2];
il void add(ll a,ll b,db P){
e[++cnt].to=b,e[cnt].nex=fir[a],e[cnt].w=P;
fir[a]=cnt;
}
il void dfs1(ll x,ll f){
r(i,x){
ll v=e[i].to;
if(v==f) continue;
dfs1(v,x);
db s=e[i].w;
s*=p[v];
p[x]=p[x]+s-p[x]*s;
}
}
il void dfs2(ll x,ll f){
r(i,x){
ll v=e[i].to;
if(v==f) continue;
db s=e[i].w;
if(1-s*p[v]) s=(p[x]-s*p[v])/(1-s*p[v]);
else s=1;
s*=e[i].w;
p[v]=p[v]+s-p[v]*s;
dfs2(v,x);
}
}
int main()
{
// file(a);
n=read();
f(i,1,n-1){
ll a=read(),b=read();
db P=read()*0.01;
add(a,b,P),add(b,a,P);
}
f(i,1,n) p[i]=read()*0.01;
dfs1(1,0);
dfs2(1,0);
db ans=0;
f(i,1,n) ans+=p[i];
printf("%.6lf\n", ans);
}