传送门

题意澄清

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

破题思路

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

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

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

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

对于每一棵子树,假设其根为 \(rt\) ,终点在其儿子 \(v\) 的子树中,如果下一步并未走向 \(v\) ,而是走向 \(u\) ,则需要花 \(2\times siz[u]\) ( \(siz[u]\) 为 \(u\) 子树内节点个数1) 步走回 \(rt\) ,那么对于在 \(v\) 之前走向 \(u\) 的概率相当于对于 \(rt\) 所有儿子的排列中 \(v\) 在 \(u\) 之后的概率,明显是 \(\frac{1}{2}\) ,故 \(u\) 子树对于步数的贡献即为 \(2\times siz[u]\times\frac{1}{2}=siz[u]\) ,所以对于以 \(rt\) 为根且以 \(rt\) 为起点的子树,其期望步数为

\[g_{rt} \gets \sum\limits_{u\in son_{rt}} ((siz_{rt}-siz_u)\times sum\_p_u+g_u)
\]

其中 \(g_u\) 为在 \(u\) 的子树中以 \(u\) 为根且以 \(u\) 为起点的期望步数,\(sum\_p_u\) 为以 \(u\) 的子树中的结点为终点的概率。

树形dp

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

先明确一下定义。

\[\begin{aligned}
& \text{依据}\ u\ \text{去更新儿子}\ v\\
& g,siz,sum\_p \text{意义同上}\\
& f_u:\text{以}\ u\ \text{为起点的期望步数}\\
& p2_u:\text{以}\ u\ \text{为终点的概率}\\
& except\_v:\text{对于}\ v\ \text{的父亲}\ u\ \text{没有子树}\ v\ \text{时}\ g_u\ \text{的值}
\end{aligned}
\]

首先我们先处理式子中的 \(g_u\) ,即 \(except\_v\) ,既然是消去影响,故是用 \(f_u\) 去减去影响。

  • 终点不在 \(v\) 的子树中

    概率为 \(1-sum\_p_v-p2_u\)

    贡献为 \(siz_v\)

    影响为 \((1-sum\_p_v-p2_u)\times(siz_v)\)
  • 终点在 \(v\) 的子树中

    概率为 \(sum\_p_v\)

    贡献为 \(n-siz_v\)

    影响为 \(sum\_p_v\times(n-siz_v)\)

\[except\_v \gets f_u - siz_v \times (1 - p2_u - sum\_p_v) - (n - siz_v) \times sum\_p_v
\]

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

  • 终点在原本 \(v\) 的子树中

    概率为 \(sum\_p_v-p2_v\)

    贡献为 \(n-siz_v\)

    期望步数为 \((sum\_p_v-p2_v)\times(b-siz_v)\)
  • 终点不在原本 \(v\) 的子树中

    模仿原 \(dp\) 式子可得 \((n-(n-siz_v))\times(1-sum\_p_v)+except\_v\)

\[f_v \gets (n - siz_v) \times (sum\_p_v - p2_v) + (n - (n - siz_v)) \times (1 - sum\_p_v) + 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

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

「CF 123E」Maze的更多相关文章

  1. Solution -「CF 1342E」Placing Rooks

    \(\mathcal{Description}\)   Link.   在一个 \(n\times n\) 的国际象棋棋盘上摆 \(n\) 个车,求满足: 所有格子都可以被攻击到. 恰好存在 \(k\ ...

  2. 「CF 600E」 Lomsat gelral

    题目链接 戳我 \(Describe\) 给出一棵树,每个节点有一个颜色,求每个节点的子树中颜色数目最多的颜色的和. \(Solution\) 这道题为什么好多人都写的是启发式合并,表示我不会啊. 这 ...

  3. 「CF 961G」Partitions

    题目链接 戳我 \(Solution\) 首先,这个直接推式子.自己推去 所以我们来想一想一些巧妙的方法 \(|S|\sum w_i\) 可以转化为:划分好集合后,每个点都对当前点有\(w_i\)的贡 ...

  4. Solution -「CF 1622F」Quadratic Set

    \(\mathscr{Description}\)   Link.   求 \(S\subseteq\{1,2,\dots,n\}\),使得 \(\prod_{i\in S}i\) 是完全平方数,并最 ...

  5. Solution -「CF 923F」Public Service

    \(\mathscr{Description}\)   Link.   给定两棵含 \(n\) 个结点的树 \(T_1=(V_1,E_1),T_2=(V_2,E_2)\),求一个双射 \(\varph ...

  6. Solution -「CF 923E」Perpetual Subtraction

    \(\mathcal{Description}\)   Link.   有一个整数 \(x\in[0,n]\),初始时以 \(p_i\) 的概率取值 \(i\).进行 \(m\) 轮变换,每次均匀随机 ...

  7. Solution -「CF 1586F」Defender of Childhood Dreams

    \(\mathcal{Description}\)   Link.   定义有向图 \(G=(V,E)\),\(|V|=n\),\(\lang u,v\rang \in E \Leftrightarr ...

  8. Solution -「CF 1237E」Balanced Binary Search Trees

    \(\mathcal{Description}\)   Link.   定义棵点权为 \(1\sim n\) 的二叉搜索树 \(T\) 是 好树,当且仅当: 除去最深的所有叶子后,\(T\) 是满的: ...

  9. Solution -「CF 623E」Transforming Sequence

    题目 题意简述   link.   有一个 \(n\) 个元素的集合,你需要进行 \(m\) 次操作.每次操作选择集合的一个非空子集,要求该集合不是已选集合的并的子集.求操作的方案数,对 \(10^9 ...

  10. Solution -「CF 1023F」Mobile Phone Network

    \(\mathcal{Description}\)   Link.   有一个 \(n\) 个结点的图,并给定 \(m_1\) 条无向带权黑边,\(m_2\) 条无向无权白边.你需要为每条白边指定边权 ...

随机推荐

  1. 解决MindSpore-2.4-GPU版本的安装问题

    问题背景 虽说在MindSpore-2.3之后的版本中不在正式的发行版中支持GPU硬件后端,但其实在开发分支版本中对GPU后端是有支持的: 但是在安装的过程中可能会遇到一些问题或者报错,这里复现一下我 ...

  2. 2023NOIP A层联测23 T2 涂鸦

    2023NOIP A层联测23 T2 涂鸦 模拟赛一道博弈,剩下仨全期望,我: 思路 其实我也不是很会 考虑设 \(f_{mst}\),为 \(n*m\) 个格被压成一个二进制 \(mst\),转移到 ...

  3. mysql8可以创建虚拟列作为公式映射字段

    普通的表 加个字段 此时再查 想改这个虚拟字段? 没门,他不能被修改.只能改那个被映射的原字段 我们看看表结构定义 好了,明白了,你就是个影分身!

  4. Linux中tar文件压缩与解压

    文件压缩与解压缩 一般什么情况下使用文件压缩? 备份数据,数据传输 节省磁盘空间 减少带宽使用 减少负载 减少IO操作 什么情况下进行压缩比较合适? 错过业务高峰期,由于文件的压缩会瞬间加大cpu的负 ...

  5. Git之清除历史记录操作

    近期公司需要将之前代码仓库中的提交记录都清理,所以操作一下,记录一下步骤: 安全考虑: 有时候在提交代码时,不小心提交了敏感数据,如账号密码什么的,这样在历史记录中就可以查看到,这样很不安全,所以就需 ...

  6. Ubuntu默认启动到字符界面

    修改/etc/default/grub sudo cp /etc/default/grub /etc/default/grub.bak sudo chmod 0777 /etc/default/gru ...

  7. Node.js Express 框架(1)

    1.Express介绍 Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具. Express的特点: 实现 ...

  8. 【分块】LibreOJ 6282 数列分块入门6

    题目 https://loj.ac/p/6282 题解 数据范围 \(1 \leq n \leq 10^5\),因此进行分块最多分 \(\sqrt{10^5} ≈ 318\) 块.且数据是随机生成的, ...

  9. NATS: 自定义服务 (Service)

    使用 NATS 服务框架来创建服务会变得非常简单,基于 NATS 的服务框架,NAST 的客户端库可以更简单的构建.服务发现和监控服务.服务框架自动将所有的订阅归类到一个队列组中,提供构建基于层次化的 ...

  10. DDD你真的理解清楚了吗?怎么准确理解“值对象”

    这些年,随着软件业的不断发展,软件系统开始变得越来越复杂而难于维护.这时,越来越多的开发团队开始选择实践DDD领域驱动设计.领域驱动设计是一种非常优秀的软件设计思想,它可以非常好地帮助我们梳理复杂业务 ...