CF123E Maze 题解
记 \(u\) 作为起点的概率为 \(q_u\) ,作为终点的概率为 \(p_u\)。
题目给的代码可以看作一个从某个点开始,以它为根 dfs 到终点的步数,这启发我们直接考虑以每个点为根,到达所有可能终点的期望步数之和。
首先可以观察出几个性质:
假设当前的根为 \(x\),终点是 \(y\),那么从 \(x\) 开始走,考虑其中的一步。
- 如果这一步进入了与 \(y\) 所在子树不同的一个子树,一定会把这个子树内的所有点遍历完才出来。假设进入了一个以 \(z\) 为根的子树,大小为 \(sz_z\),那么这个子树内的边数就是 \(sz_z-1\),所需步数即为 \(2(sz_z-1)+2=2sz_z\)。
- 如果走进了了 \(y\) 所在的子树,那么就不会再出来。
对于一个点,记 \(y\) 所在的子树的根为 \(u\)。
由上面的性质可以知道,对于一个点,如果一个它的一个儿子 \(v \not=u\) 在 \(u\) 之前被访问,那么答案就会加上 \(sz_v\)。而 \(v\) 在 \(u\) 之前被访问的概率相当于给这个点的儿子随机一个排列,其中 \(v\) 在 \(u\) 之前的概率。考虑对于每种 \(v\) 在 \(u\) 之前的排列,swap(u,v)
之后就会得到一个 \(u\) 在 \(v\) 之前的排列,所以概率是 \(\dfrac 12\)。
接下来设 \(S\) 为 \((x,fa_y)\) 这条路径上的点分叉出去的点集的并,那么 \(x\) 走到 \(y\) 的期望就是 \(\sum\limits_{u \in S} \dfrac{2sz_u}{2}\),对答案的贡献就是 \(q_xp_y\sum\limits_{u \in S}sz_u\)。
接下来就考虑对于每个根 \(x\) 求出 \(p_y\sum\limits_{u \in S}sz_u\),先考虑以 \(1\) 为根。
设 \(f_u\) 表示以 \(u\) 为起点,到达 \(u\) 子树的的所有可能终点的期望步数之和,设 \(s=\sum\limits_{v \in son_u} sz_v=sz_u-1\),\(t_u=\sum\limits_{v\in Subtree_u} p_u\)。
那么转移就是 \(f_u=\sum\limits_{v \in son_u} (s-sz_v+1)t_v+f_v\)。
换根也非常容易,首先对于每个点的 \(\sum t_v\) 可以提前维护出来,这样子 \(s\) 的变化导致的答案增量也很容易算出,具体的转移可以见代码。
复杂度 \(O(n)\)。
代码:
const int N=2e5+50;
int n,x[N],y[N],head[N],cnt;
ld sum[N],sumt[N],sz[N],t[N],p[N],q[N],f[N],g[N],ans;
struct edge{int to,nxt;}e[N<<1];
inline void addedge(int u,int v){e[++cnt]=(edge){v,head[u]},head[u]=cnt;}
void dfs1(int u,int fa)
{
sz[u]=1,t[u]=p[u];
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa) continue;
dfs1(v,u),sz[u]+=sz[v],t[u]+=t[v];
sum[u]+=sz[v],sumt[u]+=t[v];
}
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa) continue;
f[u]+=f[v]+(sum[u]-sz[v]+1)*t[v];
}
}
void dfs2(int u,int fa)
{
ans+=g[u]*q[u];
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa) continue;
ld fu=g[u]-sz[v]*(1-p[u])-(n-1-2*sz[v]+1)*t[v]-f[v];
g[v]=f[v]+(n-sz[v])*sumt[v]+(n-1-(n-sz[v])+1)*(1-t[v])+fu,dfs2(v,u);
}
}
int main(void)
{
n=read();int u,v,sumx=0,sumy=0;
fr(i,2,n) u=read(),v=read(),addedge(u,v),addedge(v,u);
fr(i,1,n) x[i]=read(),y[i]=read(),sumx+=x[i],sumy+=y[i];
fr(i,1,n) q[i]=(ld)x[i]/(ld)sumx,p[i]=(ld)y[i]/(ld)sumy;
dfs1(1,0),g[1]=f[1],dfs2(1,0);
printf("%.10Lf\n",ans);
return 0;
}