嘘~ 正在从服务器偷取页面 . . .

CF123E Maze 题解


CF123E Maze 题解

题目链接:Maze

题意

一棵 $n$ 个点的树,起点和终点随机选取(概率给定)。

运行如下程序, $V(x)$ 是与 $x$ 相邻的顶点列表,最初 $\mathrm{flag}$ 数组为 $\verb!false!$,DFS 的 参数是起点

你要求 $\mathrm{count}$ 的期望值。

DFS(x)
    if x == exit vertex then
        finish search
    flag[x] <- TRUE
    random shuffle the vertices' order in V(x) 
    // here all permutations have equal probability to be chosen
    for i <- 1 to length[V] do
        if flag[V[i]] = FALSE then
            count++;
            DFS(y);
    count++;

输入格式

第一行给定 $n$ ,接下来 $n-1$ 行给定边

然后 $n$ 行给定每个点作为 $S,T$ 的概率(给定整数 $x_i,y_i$ ,概率为 $\frac{x_i}{\sum_j x_j}$ 和 $\frac{y_i}{\sum_j y_j}$​)。

输出格式

误差不超过 $10^{-9}$ 。

数据范围

$1 \le n \le 10^5$​ 。

注意这个题目的代码,对于一些点会有额外的“回程费”,而另一些则没有。

考虑一对 $(S,T)$ ,将 $S$ 临时定为根,则

  • 对于 $T$ 子树内的点,这些点不会被遍历到,贡献为 $0$ 。

  • 对 $S \leadsto T$ 的最短路径上的点,这些点只会被遍历一次(不回程),贡献为 $1$ 。

  • 对于除此以外的其他点,例如点 $x$ ,当我们走到 $fa(x)$ 时,

    • 有 $\frac{1}{2}$ 的概率走到 $x$ ,此时 $x$ 的贡献为 $2$​​ ;
    • 还有 $\frac{1}{2}$ 的概率走到 $T$ 的方向,此时 $x$ 的贡献为 $0$ 。

    于是这样的 $x$ 的贡献为 $\frac{1}{2}\times 2 + \frac{1}{2}\times 0 = 1$ 。

    这里的 $\frac{1}{2}$ 是考察有多少种排列中 $x$ 在 $T$ 方向那个节点的前面。

于是问题就变成了,对于每一对 $(S,T)$ ,以 $S$ 为根时 $n-\mathrm{size}_S(T)$ 的期望。

考虑枚举 $T$ ,然后统计 $S$ ,

  • $S=T$ ,此时 $\mathrm{size}_S(T)=n$ 。
  • $S$ 在 $T$ 的子树内,显然 $\mathrm{size}_S(T) = n - \mathrm{size}(S)$ 。
  • $S$ 不在 $T$ 的子树内,则 $\mathrm{size}_S(T)=\mathrm{size}(T)$​ 。

那么我们只需要扫一遍,用每种 $S$ 的概率 $\times$ 它的贡献,加起来以后乘上当前节点作为 $T$ 的概率即可。

时间复杂度 $\mathcal{O}(n)$

代码:

#include <bits/stdc++.h>
using namespace std;
// #define int long long
// #define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e5 + 15))

int n, _ps[N], _pt[N], sz[N]; 
double S, T, res, ps[N], pt[N], p[N];
vector<int> vec[N];
void dfs(int u, int fa)
{
    sz[u] = 1; p[u] = ps[u];
    double sum = 0;
    for(int v : vec[u]) if(v != fa)
    {
        dfs(v, u); sz[u] += sz[v];
        p[u] += p[v]; sum += p[v] * sz[v];
    }
    sum += (1 - p[u]) * (n - sz[u]); res += sum * pt[u];
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n;
    for(int i = 1, u, v; i < n; i++)
    {
        cin >> u >> v;
        vec[u].push_back(v); vec[v].push_back(u);
    }
    for(int i = 1; i <= n; i++) { 
        cin >> _ps[i] >> _pt[i]; S += _ps[i]; T += _pt[i];
    }
    for(int i = 1; i <= n; i++) {
        ps[i] = _ps[i] / S; pt[i] = _pt[i] / T;
    }
    dfs(1, 0); cout << fixed << setprecision(12) << res << '\n';
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/article/j0en6wyv


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录