• CodeForces


    http://codeforces.com/problemset/problem/123/E

    题目翻译:(翻译来自: http://www.cogs.pw/cogs/problem/problem.php?pid=1734

    一个迷宫是一棵树(即一张无向图,其中任意两点之间仅有一条路径)。迷宫的起点和终点都按照某种概率随机选取。人们会在迷宫中用深度优先搜索的方法搜寻终点。如果有许多条可能的路径,会等概率地选取一条。考虑如下伪代码:

    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++;

    V(x)是和x相邻的顶点列表。最初flag数组的值均为false。第一次DFS的参数是迷宫的入口节点。当搜索终止,变量count的值就是在迷宫中走的步数。

    你的任务是计算在迷宫中从入口到出口,所走的期望步数。

    题解:

      首先一看知道题就知道应该去求每个点对答案的贡献

    现在考虑如何求出这个贡献。

    我们发现如果我们单独枚举一个点,那么这个点的情况一共有三种:

    1.必定被经过 

     2.必定不被经过

     3.不一定被经过

    这样就不好统计了,所以我们先转换一下思维

    怎样才能准确的找出经过的路径? 最直接的方法就是暴力枚举所有的起点和终点

    这样路径就唯一确定了,直接在这个图中求一边期望,然后加权求和

    可是 n <= 10^5,暴力肯定无法通过,但是有值得我们借鉴的思维

     

    枚举端点

    我们可以枚举终点,这样情况就分成了。。。还是三种:

     1.起点就是终点,贡献为0,该情况可以忽略

     2.起点在以终点为根的子树中

     3.起点不在以终点为根的子树中

    对于第二种情况,我们可以直接计算贡献,即(子树的节点数目*子树中有一个点作为入口的概率)

    对于第三种情况,我们也可以直接计算的,即(除去子树后树的节点数目*除去子树后树中存在入口的概率)

    为什么能这么计算呢?

    我们考虑一下,如果根的某一个儿子是入口,那么贡献应为0.5*siz[v]*2

    我们发现,如果一条路径经过n个点两次后到达出口,那么对答案的贡献即为(n<<1)

    我们可以这么想,把题目中给的伪代码改动一下,那么每次到达一个点++count,退出一个点++count

    最后再--count,这样就是这条路径对答案的贡献。

    所以,我们直接刨去到达终点时对答案做出的贡献,那么就可以得到刚才的式子了

    我们考虑进行推广,发现所有的点都满足这个式子

    所以我们可以直接对若干个点直接加和,那么就是上面提到的计算方式了

    O(n)

    Code

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 using namespace std;
     5 typedef long long ll;
     6 inline void read(int &x){
     7 x=0;char ch;bool flag = false;
     8 while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
     9     while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
    10 }
    11 inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
    12 inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
    13 const int maxn = 100010;
    14 struct Edge{
    15     int to,next;
    16 }G[maxn<<1];
    17 int head[maxn],cnt;
    18 void add(int u,int v){
    19     G[++cnt].to = v;
    20     G[cnt].next = head[u];
    21     head[u] = cnt;
    22 }
    23 int siz[maxn],ex[maxn],en[maxn];
    24 int fa[maxn],n;
    25 ll sn=0,sx=0,ans = 0;
    26 #define v G[i].to
    27 void dfs(int u){
    28     siz[u] = 1;
    29     for(int i = head[u];i;i=G[i].next){
    30         if(v == fa[u]) continue;
    31         fa[v] = u;
    32         dfs(v);
    33         siz[u] += siz[v];
    34         en[u] += en[v];
    35         ans += 1LL*ex[u]*en[v]*siz[v];
    36     }ans += 1LL*ex[u]*(sn - en[u])*(n - siz[u]);
    37 }
    38 #undef v
    39 int main(){
    40     read(n);
    41     for(int i=1,u,v;i<n;++i){
    42         read(u);read(v);
    43         add(u,v);add(v,u);
    44     }
    45     for(int i=1;i<=n;++i){
    46         read(en[i]),read(ex[i]);
    47         sn += en[i];sx += ex[i];
    48     }dfs(1);
    49     printf("%.60lf
    ",(double)ans/(1.0*sn*sx) );
    50     getchar();getchar();
    51     return 0;
    52 }
  • 相关阅读:
    第1章 数据类型 第1节 JavaScript中的几个重要概念
    第1章 数据类型 第3节 JavaScript数值类型(2)
    第1章 数据类型 第4节 JavaScript字符类型
    GDI编程框架代码
    linux清空日志文件内容 比如log日志
    快速点对点批量向员工推送企业微信消息的方法
    在EasySQLMAIL中实现表格的行列转置
    快速信息分发技巧:按员工手机号通过企业微信点对点分发信息
    网络
    【odoo】【相识篇】一、Odoo VS SAP?
  • 原文地址:https://www.cnblogs.com/Skyminer/p/6261443.html
Copyright © 2020-2023  润新知