zoukankan      html  css  js  c++  java
  • CodeForces

    Description

    给你一棵树,边权都是 1,每一个点有一个是起点的概率和一个是终点的概率,你将以起点为根,开始在树上随机 dfs,每到一个点,就会将他的所有儿子随机打乱成序列,然后按照那个随机顺序走完,直到走到终点。求 dfs 从起点到终点的期望长度。

    前言

    MD 我是 SB,我要疯了。

    Solution

    由于是 DFS 序,假设我们求的是 x 到 y(y 是 x 的儿子)的路径期望,如果走到另外一个儿子 z,要再走 (mathtt{2*siz[z]}) 才能会到 x。每个儿子在 y 的前面的概率为 (mathtt{frac{1}{2}})。(因为要么是 y 在前,要么是 z 在前)所以每个 z 对于路径期望都会贡献 (mathtt{siz[z]})

    所以 x 到 y 的路径期望是:(mathtt{siz[x]-siz[y]-1+1})。前面的 (mathtt{siz[x]-siz[z]}) 就不多说了,后面减去一是因为多算了 x 这个节点,又加上一是因为 x 到 y 有一条边的距离,而这条路的经过概率是 1。

    由此观之啊呸,如果我们要计算的是 y 到 x 的路径期望(定义同上),可以把 x 看作 y 的儿子,我们就可以直接套之前的结论,答案就是:(mathtt{siz[y]})

    最后,我们发现如果计算 u 到 x 的路径期望(其中 u 可以是 y 子树中的任意一点),答案也是 (mathtt{siz[y]})。(就一层层累加就好了)

    这样,我们的算法应运而出:枚举 x 作为终点,再枚举 x 的儿子 y 作为起始点所在子树的代表,这样就可以算出以 x 子树中的点为起点,x 为终点的路径期望。但是,我们还要计算以 x 子树外的点为起点,x 为终点的路径期望。(后面这个部分也用和之前一样的“作案手法”,将 x 的父亲看成 x 的儿子,又可以用之前那个结论直接求了 QwQ)

    这样求解的时间复杂度应该是 (mathtt{O(n)}) 级别的,毕竟一个人只有一个爸爸嘛。

    Code

    #include <cstdio>
    
    const int N = 100005;
    
    int n, cnt, head[N], dot[N << 1], nxt[N << 1], fa[N], siz[N];
    double in[N], out[N], si, so, ans;
    
    int read() {
        int x = 0, f = 1; char s;
        while((s = getchar()) < '0' || s > '9') if(s == '-') f = -1;
        while(s >= '0' && s <= '9') {x = (x << 1) + (x << 3) + (s ^ 48); s = getchar();}
        return x * f;
    }
    
    void addEdge(const int u, const int v) {
        dot[++ cnt] = v, nxt[cnt] = head[u], head[u] = cnt;
    }
    
    void dfs(const int u) {
        siz[u] = 1;
        for(int i = head[u]; i; i = nxt[i]) {
            int v = dot[i];
            if(v == fa[u]) continue;
            fa[v] = u;
            dfs(v);
            siz[u] += siz[v]; in[u] += in[v];
        }
    }
    
    int main() {
        n = read();
        for(int i = 1; i < n; ++ i) {
            int u = read(), v = read();
            addEdge(u, v), addEdge(v, u);
        }
        for(int i = 1; i <= n; ++ i) {
            in[i] = read(), out[i] = read();
            si += in[i]; so += out[i];
        }
        for(int i = 1; i <= n; ++ i) in[i] /= si;
        dfs(1);
        for(int u = 1; u <= n; ++ u) {
            for(int i = head[u]; i; i = nxt[i]) {
                int v = dot[i];
                if(fa[u] == v) ans += (1 - in[u]) * (n - siz[u]) * out[u] / so;
                else ans += in[v] * siz[v] * out[u] / so;
            }
        }
        printf("%.11f
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    苹果新的编程语言 Swift 语言进阶(五)--控制流
    苹果新的编程语言 Swift 语言进阶(四)--字符串和收集类型
    苹果新的编程语言 Swift 语言进阶(三)--基本运算和扩展运算
    苹果新的编程语言 Swift 语言进阶(二)--基本类型
    第一篇 android架构是如何满足设计目标的?
    第三篇 android 应用开发模式之MVC模式及Observer模式
    第二篇 android应用开发模式之模板模式
    为什么带网格(mesh)的模型添加了刚体Rigidbody和MeshCollider,还是会从地板穿过去?
    Mecanim动画模型规范
    HTC Vive 体验的折腾经历
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/12692753.html
Copyright © 2011-2022 走看看