[CF 123E] Maze

传送门

题意澄清

对于 d f s dfs dfs 遍历时,在某一个点进入子树的顺序并不是按输入顺序,而是假定随机选择未进入过的子树 (这纠结了我好久) 。

破题思路

首先可以明确这题不能推一个 O ( 1 ) O(1) O(1) 的式子来计算期望 (树的结构是随机的,对于所有点不存在均摊期望的可能) ,但是对于某一刻子树以根节点为起点时,一定是存在一个快速计算期望的式子 (不可能枚举起点还要枚举终点吧) 。

其次,考虑到需要对于每一个点作起点求出期望步数,又是一棵树,故树形 d p dp dp 肯定是必要的,同时还需要换根。

那么算法分析完了(树形 d p dp dp + + + 推以子树根作起点的步数期望式子),就要具体实现算法了

以子树根作起点的期望步数

对于每一棵子树,假设其根为 r t rt rt ,终点在其儿子 v v v 的子树中,如果下一步并未走向 v v v ,而是走向 u u u ,则需要花 2 ∗ s i z [ u ] 2*siz[u] 2siz[u] ( s i z [ u ] siz[u] siz[u] u u u 子树内节点个数1) 步走回 r t rt rt ,那么对于在 v v v 之前走向 u u u 的概率相当于对于 r t rt rt 所有儿子的排列中 v v v u u u 之后的概率,明显是 1 2 \frac{1}{2} 21 ,故 u u u 子树对于步数的贡献即为 2 ∗ s i z [ u ] ∗ 1 2 = s i z [ u ] 2*siz[u]*\frac{1}{2}=siz[u] 2siz[u]21=siz[u] ,所以对于以 r t rt rt 为根且以 r t rt rt 为起点的子树,其期望步数为
g r t = ∑ u ∈ s o n r t ( s i z r t − s i z u ) ∗ s u m _ p u + g u g_{rt}=\sum\limits_{u\in son_{rt}} (siz_{rt}-siz_u)*sum\_p_u+g_u grt=usonrt(sizrtsizu)sum_pu+gu
其中 g u g_u gu 为在 u u u 的子树中以 u u u 为根且以 u u u 为起点的期望步数, s u m _ p u sum\_p_u sum_pu 为以 u u u 的子树中的结点为终点的概率。

树形dp

对于第一次树形 d p dp dp 的式子我们已经在上一个部分推出来了,难点在于换根时的式子。我经常使用的方法是不去思考意义,只思考原式的 “逆式”

先明确一下定义
依据  u  去更新儿子  v g , s i z , s u m _ p 意义同上 f u : 以  u  为起点的期望步数 p 2 u : 以  u  为终点的概率 e x c e p t _ v : 对于  v  的父亲  u  没有子树  v  时  g u  的值 \begin{aligned} & 依据\ u\ 去更新儿子\ v\\ & g,siz,sum\_p 意义同上\\ & f_u:以\ u\ 为起点的期望步数\\ & p2_u:以\ u\ 为终点的概率\\ & except\_v:对于\ v\ 的父亲\ u\ 没有子树\ v\ 时\ g_u\ 的值 \end{aligned} 依据 u 去更新儿子 vg,siz,sum_p意义同上fu: u 为起点的期望步数p2u: u 为终点的概率except_v:对于 v 的父亲 u 没有子树 v  gu 的值

首先我们先处理式子中的 g u g_u gu ,即 e x c e p t _ v except\_v except_v ,既然是消去影响,故是用 f u f_u fu 去减去影响

  • 终点不在 v v v 的子树中
    概率为 1 − s u m _ p v − p 2 u 1-sum\_p_v-p2_u 1sum_pvp2u
    贡献为 s i z v siz_v sizv
    影响为 ( 1 − s u m _ p v − p 2 u ) ∗ ( s i z v ) (1-sum\_p_v-p2_u)*(siz_v) (1sum_pvp2u)(sizv)
  • 终点在 v v v 的子树中
    概率为 s u m _ p v sum\_p_v sum_pv
    贡献为 n − s i z v n-siz_v nsizv
    影响为 s u m _ p v ∗ ( n − s i z v ) sum\_p_v*(n-siz_v) sum_pv(nsizv)

e x c e p t _ v = f u − s i z v ∗ ( 1 − p 2 u − s u m _ p v ) − ( n − s i z v ) ∗ s u m _ p v except\_v=f_u - siz_v * (1 - p2_u - sum\_p_v) - (n - siz_v) * sum\_p_v except_v=fusizv(1p2usum_pv)(nsizv)sum_pv

接下来我们处理以 v v v 为起点的期望步数

  • 终点在原本 v v v 的子树中
    概率为 s u m _ p v − p 2 v sum\_p_v-p2_v sum_pvp2v
    贡献为 n − s i z v n-siz_v nsizv
    期望步数为 ( s u m _ p v − p 2 v ) ∗ ( b − s i z v ) (sum\_p_v-p2_v)*(b-siz_v) (sum_pvp2v)(bsizv)
  • 终点不在原本 v v v 的子树中
    模仿原 d p dp dp 式子可得 ( n − ( n − s i z v ) ) ∗ ( 1 − s u m _ p v ) + e x c e p t _ v (n-(n-siz_v))*(1-sum\_p_v)+except\_v (n(nsizv))(1sum_pv)+except_v

f v = ( n − s i z v ) ∗ ( s u m _ p v − p 2 v ) + ( n − ( n − s i z v ) ) ∗ ( 1 − s u m _ p v ) + e x c e p t _ v f_v=(n - siz_v) * (sum\_p_v - p2_v) + (n - (n - siz_v)) * (1 - sum\_p_v) + except\_v fv=(nsizv)(sum_pvp2v)+(n(nsizv))(1sum_pv)+except_v

实现

综合上述内容,便有了以下代码

/*
address:https://codeforces.com/problemset/problem/123/E
AC 2024/8/28 12:26
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
vector<int>G[N];
int n;
double p1[N], p2[N];
int sum1, sum2;
double f[N], g[N], sum_p[N];
int siz[N];
inline void dfs1(int u, int fa) {
    sum_p[u] = p2[u];
    siz[u] = 1;
    for (auto v : G[u]) {
        if (v == fa) continue;
        dfs1(v, u);
        sum_p[u] += sum_p[v];
        siz[u] += siz[v];
    }
    for (auto v : G[u]) {
        if (v == fa) continue;
        g[u] += (siz[u] - siz[v]) * sum_p[v] + g[v];
    }
}
inline void dfs2(int u, int fa) {
    for (auto v : G[u]) {
        if (v == fa) continue;
        double except_v = f[u] - siz[v] * (1 - p2[u] - sum_p[v]) - (n - siz[v]) * sum_p[v];
        f[v] = (n - siz[v]) * (sum_p[v] - p2[v]) + (n - (n - siz[v])) * (1 - sum_p[v]) + except_v;
        dfs2(v, u);
    }
}
int main() {
    scanf("%d", &n);
    for (int i = 1;i < n;i++) {
        int u, v;scanf("%d%d", &u, &v);
        G[u].push_back(v);G[v].push_back(u);
    }
    for (int i = 1;i <= n;i++) scanf("%lf%lf", &p1[i], &p2[i]), sum1 += p1[i], sum2 += p2[i];
    for (int i = 1;i <= n;i++) p1[i] /= sum1, p2[i] /= sum2;
    dfs1(1, 0);
    f[1] = g[1];
    dfs2(1, 0);
    double ans = 0;
    for (int i = 1;i <= n;i++) ans += p1[i] * f[i];
    printf("%.12lf", ans);
    return 0;
}

The end

一道概率期望加树形 d p dp dp 的好题,具备一定思维难度,但又可以一步步推出式子,对于学未至极但又学习良多的我是不多的提升较大的练习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值