zoukankan      html  css  js  c++  java
  • [树形dp] Jzoj P5814 树

    Description

    梦游中的你来到了一棵 N 个节点的树上. 你一共做了 Q 个梦, 每个梦需要你从点 u 走到 点 v 之后才能苏醒, 由于你正在梦游, 所以每到一个节点后,你会在它连出去的边中等概率地 选择一条走过去, 为了确保第二天能够准时到校, 你要求出每个梦期望经过多少条边才能苏 醒. 为了避免精度误差, 你要输出答案模10^9 + 7的结果.
     

    Input

    第一行两个整数分别代表 N 和 Q. 接下来 N-1 行, 每行两个整数 u, v 代表树中的一条边. 接下来 Q 行, 每行两个整数代表询问的 u,v. 

    Output

    一共 Q 行, 每行一个整数代表答案
     

    Sample Input

    4 2
    1 2
    2 3
    3 4
    1 4
    3 4

    Sample Output

    9
    5 
     

    Data Constraint

    对于 20%的数据, N <= 10.
    对于 40%的数据, N <= 1000.
    另有 20%的数据, 保证给定的树是一条链.
    对于 100%的数据, N <= 100000, Q <= 100000. 
     

    Hint

    题解

    • 首先,设d[i]为i的出度,f[i]为从i走向根节点的期望步数,g[i]为从根节点走到i的期望步数
    • 那么,f[i]=1/d[i]+Σ(f[i]+f[son]+1)/d[i]
    • 其实就是
    • ①直接走到父亲的期望1/d[i]
    • ②走到儿子,然后走回i,再到父亲的期望
    • 那么,g[i]=1/d[fa[i]]+(1+g[i]+g[fa[i]])/d[fa[i]]+Σ(son!=i)(1+g[i]+f[son])/d[fa[i]]
    • 其实就是
    • ①直接走到i的期望1/d[i]
    • ②走到爷爷,然后走回父亲,再走到i的期望
    • ③走到非i的儿子,然后走回父亲,再走到i的期望
    • 然后将f[i]化简一下:
    • f[i]=1/d[i]+Σ(f[i]+f[son]+1)/d[i]
    • d[i]*f[i]=1+Σ(f[i]+f[son]+1)
    • d[i]*f[i]=1+(d[i]-1)*f[i]+Σf[son]+d[i]-1
    • (d[i]-d[i]+1)*f[i]=d[i]+Σf[son]
    • f[i]=d[i]+Σf[son]
    • 再化简一下g[i]:
    • g[i]=1/d[fa[i]]+(1+g[i]+g[fa[i]])/d[fa[i]]+Σ(son!=i)(1+g[i]+f[son])/d[fa[i]]
    • d[fa[i]]*g[i]=1+1+g[i]+g[fa[i]]+Σ(son!=i)(1+g[i]+f[son])
    • d[fa[i]]*g[i]=2+g[i]+g[fa[i]]+(d[i]-2)+(d[i]-2)*g[i]+Σf[son]
    • d[fa[i]]*g[i]=d[i]+(d[i]-1)*g[i]+g[fa[i]]+Σf[son]
    • g[i]=d[i]+g[fa[i]]+Σf[son]
    • 然后就用dfs预处理出这两个数组
    • f[i]=f[i]+f[fa],g[i]=g[i]+g[fa],求到最大的祖先的期望步数
    • 对于一组询问u,v,路径就是u到lca,和v到lca
    • 那么ans=f[u]-f[lca]+g[v]-g[lca]

    代码

     1 #include <cstdio> 
     2 #include <iostream>
     3 #include <cstring>
     4 #include <cmath>
     5 using namespace std;
     6 const long long mo=1e9+7;
     7 struct edge {int to,from;}e[1000010*2];
     8 int n,q,head[1000010],cnt;
     9 long long sum,f[1000010][20],deep[1000010],ans,k1[1000010],k2[1000010];
    10 void insert(int x,int y) {e[++cnt].to=y; e[cnt].from=head[x]; head[x]=cnt;}
    11 void dfs(int x,int fa)
    12 {
    13     f[x][0]=fa;
    14     k1[x]=0;
    15     for (int i=head[x];i;i=e[i].from)
    16         if (e[i].to!=fa)
    17         {
    18             dfs(e[i].to,x);
    19             k1[x]=(k1[x]+k1[e[i].to]+1)%mo;
    20         }
    21     k1[x]=(k1[x]+1)%mo;
    22 }
    23 void dfs1(int x,int fa)
    24 {
    25     long long sum=0;
    26     for (int i=head[x];i;i=e[i].from)
    27         if (e[i].to!=fa) sum=(sum+k1[e[i].to]+1)%mo;
    28         else sum=(sum+k2[x]+1)%mo;
    29     for (int i=head[x];i;i=e[i].from)
    30         if (e[i].to!=fa)
    31         {
    32             k2[e[i].to]=((sum-k1[e[i].to])%mo+mo)%mo;
    33             dfs1(e[i].to,x);
    34         }
    35 }
    36 void dfs2(int x,int fa)
    37 {
    38     deep[x]=deep[fa]+1;
    39     k1[x]=(k1[x]+k1[fa])%mo,k2[x]=(k2[x]+k2[fa])%mo;
    40     for (int i=head[x];i;i=e[i].from)
    41         if (e[i].to!=fa)
    42             dfs2(e[i].to,x);
    43 }
    44 int getlca(int u,int w)
    45 {
    46     if (deep[u]<deep[w]) swap(u,w);
    47     int d=deep[u]-deep[w];
    48     if (d) for (int i=0;i<=log(n)/log(2)+1&&d;i++,d>>=1) if (d&1) u=f[u][i];
    49     if (u==w) return u;
    50     for (int i=log(n)/log(2)+1;i>=0;i--)
    51         if (f[u][i]!=f[w][i])
    52             u=f[u][i],w=f[w][i];
    53     return f[u][0];
    54 }
    55 int main()
    56 {
    57     freopen("tree.in","r",stdin);
    58     freopen("tree.out","w",stdout);
    59     scanf("%d%d",&n,&q);
    60     for (int i=1;i<=n-1;i++)
    61     {
    62         int u,v;
    63         scanf("%d%d",&u,&v);
    64         insert(u,v),insert(v,u);
    65     }
    66     dfs(1,-1);
    67     k1[1]=k2[1]=0;
    68     dfs1(1,-1); 
    69     dfs2(1,-1);
    70     for (int i=1;i<=log(n)/log(2)+1;i++)
    71         for (int j=1;j<=n;j++)
    72             f[j][i]=f[f[j][i-1]][i-1];
    73     for (int i=1;i<=q;i++)
    74     {
    75         int u,v;
    76         scanf("%d%d",&u,&v);
    77         int lca=getlca(u,v);
    78         printf("%lld
    ",(k1[u]-k1[lca]+k2[v]-k2[lca]+mo)%mo);
    79     }
    80     return 0;
    81 }
  • 相关阅读:
    环境搭建-----IntelliJ idea之resin配置
    Maven的pom.xml文件详解------Build Settings
    JVM调优总结
    JAVA基础-栈与堆,static、final修饰符、内部类和Java内存分配
    JVM之字节码——Class文件格式
    java中的深复制和浅复制
    java创建对象的四种方式
    iOS获取所有机型
    博客已停止更新,请移步简书
    NSCache的简单使用
  • 原文地址:https://www.cnblogs.com/Comfortable/p/9475585.html
Copyright © 2011-2022 走看看