Tag : 换根 dp
引入 :
f
[
u
]
:
f[u] :
f[u]: 在
u
u
u 子树内的答案
g
[
u
]
:
g[u]:
g[u]: 以
u
u
u 为根整体的答案
做换根 dp 要算清楚转移根的过程,算清楚减了什么,加了什么
- 以 中心 为例
若当前是以 u u u 为根,先要转移为以 u u u 的儿子 v v v 为根,那么如图所示,
会影响粉色和绿色部分,粉色部分会少走图中标注的橙色边 ( − w 1 ∗ s i z e [ v ] -w_1*size[v] −w1∗size[v]),绿色部分会多走图中标注的蓝色边 ( + w 2 ∗ ( n − s i z e [ v ] ) +w_2*(n-size[v]) +w2∗(n−size[v])),所以 g [ v ] = g [ u ] − w 1 ∗ s i z e [ v ] + w 2 ∗ ( n − s i z e [ v ] ) g[v]=g[u]-w_1*size[v]+w_2*(n-size[v]) g[v]=g[u]−w1∗size[v]+w2∗(n−size[v])
//中心
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+5;
int n, f[MAXN], g[MAXN], siz[MAXN];
struct Edge{
int to,w1,w2;
};
vector <Edge> G[MAXN];
void dfs1(int u,int fa)
{
siz[u]=1;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].to, w=G[u][i].w1;
if(v==fa) continue;
dfs1(v,u);
siz[u]+=siz[v];
f[u]+= f[v] + w * siz[v];
}
}
void dfs2(int u,int fa)
{
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].to;
if(v==fa) continue;
g[v] = g[u] - G[u][i].w1 * siz[v] + G[u][i].w2 * (n-siz[v]);
dfs2(v,u);
}
}
signed main()
{
cin>>n;
for(int i=1,u,v,w1,w2;i<n;i++)
{
cin>>u>>v>>w1>>w2;
G[u].push_back((Edge){v,w1,w2});
G[v].push_back((Edge){u,w2,w1});
}
dfs1(1,0); g[1]=f[1];
dfs2(1,0);
int res=g[1];
for(int i=1;i<=n;i++)
res=min(res,g[i]);
cout<<res;
return 0;
}
正文
对于一个节点,其是否能通电取决于三者: 儿子,父亲,自身
- 自己通电
- 儿子通电,且儿子与该节点的边能通电
- 父亲通电,且父亲与该节点的边能通电
f
[
u
]
f[u]
f[u] 表示在
u
u
u 子树内,
u
u
u 通电的概率
g
[
u
]
g[u]
g[u] 表示以
u
u
u 为根,
u
u
u 通电的概率 (不光只靠儿子,还靠父亲)
-
先考虑 f [ u ] f[u] f[u]
-
f [ u ] f[u] f[u] 只可能自己通电或被儿子通电
-
我们记 自己通电概率为 A A A ,靠儿子通电概率为 B B B ,两者其中至少一个发生 u u u 即通电
-
两者不发生的概率为 ( 1 − A ) ∗ ( 1 − B ) (1-A)*(1-B) (1−A)∗(1−B) ,那么至少一个发生的概率即为 1 − ( 1 − A ) ∗ ( 1 − B ) 1-(1-A)*(1-B) 1−(1−A)∗(1−B) 为 A + B − A ∗ B A+B-A*B A+B−A∗B
void dfs1(int u,int fa)
{
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].to; double w=G[u][i].w;
if(v==fa) continue;
dfs1(v,u);
double A=f[u], B=f[v]*w;
f[u] = A+B-A*B;
}
}
//main
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i]=0.01*a[i]; f[i]=a[i];
}
dfs1(1,0);
- 接着考虑 g [ u ] g[u] g[u]
- 假设当前以 u u u 为根,现需转移为以 v v v 为根
- 我们记 u u u 不靠 v v v 及 v v v 子树通电的概率为 x [ u ] x[u] x[u]
- 现转移:
-
可得 g [ v ] = f [ v ] + w ∗ x [ u ] − f [ v ] ∗ w ∗ x [ u ] g[v]=f[v]+w*x[u]-f[v]*w*x[u] g[v]=f[v]+w∗x[u]−f[v]∗w∗x[u], w w w 为边 u , v u,v u,v 通电的概率
-
那么求出 x [ u ] x[u] x[u] 即可转移
-
由于 g [ u ] = x [ u ] + f [ v ] ∗ w − x [ u ] ∗ f [ v ] ∗ w g[u]=x[u]+f[v]*w-x[u]*f[v]*w g[u]=x[u]+f[v]∗w−x[u]∗f[v]∗w , w w w 为边 u , v u,v u,v 通电的概率
-
得 x [ u ] = ( g [ u ] − f [ v ] ∗ w ) / ( 1 − f [ v ] ∗ w ) x[u]=(g[u]-f[v]*w)/(1-f[v]*w) x[u]=(g[u]−f[v]∗w)/(1−f[v]∗w)
-
即可转移 g [ v ] g[v] g[v]
void dfs2(int u,int fa)
{
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].to; double w=G[u][i].w;
if(v==fa) continue;
if(1 - f[v] * w <= eps) g[v]=f[v];
else
{
double A = w * (g[u] - f[v] * w) / (1 - f[v] * w);
double B = f[v];
g[v] = A+B-A*B;
}
dfs2(v,u);
}
}
#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+5;
const double eps = 1e-9;
int n;
double a[MAXN], f[MAXN], g[MAXN];
struct Node{
int to;
double w;
};
vector <Node> G[MAXN];
void dfs1(int u,int fa)
{
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].to; double w=G[u][i].w;
if(v==fa) continue;
dfs1(v,u);
double A=f[u], B=f[v]*w;
f[u] = A+B-A*B;
}
}
void dfs2(int u,int fa)
{
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].to; double w=G[u][i].w;
if(v==fa) continue;
if(1 - f[v] * w <= eps) g[v]=f[v];
else
{
double A = w * (g[u] - f[v] * w) / (1 - f[v] * w);
double B = f[v];
g[v] = A+B-A*B;
}
dfs2(v,u);
}
}
int main()
{
cin>>n;
for(int i=1;i<n;i++)
{
int u,v,w;
cin>>u>>v>>w;
G[u].push_back((Node){v,1.0*w/100});
G[v].push_back((Node){u,1.0*w/100});
}
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i]=0.01*a[i]; f[i]=a[i];
}
dfs1(1,0);
g[1]=f[1];
dfs2(1,0);
double ans=0;
for(int i=1;i<=n;i++)
ans+=g[i];
printf("%.6lf",ans);
}