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

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 !
评论
  目录