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.


Solution

一开始的想法是直接求概率,然后在算期望次数,然而并不用这么麻烦。
我们设fifif_i表示节点iii走到他的父亲的期望步数,gi" role="presentation">gigig_i表示它的父亲走到它的期望步数。
先考虑如何求fff。
对于叶子节点它的fi=1" role="presentation">fi=1fi=1f_i=1。
那其他的节点怎么求?
设didid_i表示节点i的度数。
对于节点i有1di1di\frac{1}{d_i}的概率一步走到它的父亲,还有1di1di\frac{1}{d_i}走到他的一个儿子j花费总共fj+fi+1fj+fi+1f_j+f_i+1(1是有i走的j的花费)步走到i的父亲。
那么我们得到fi=1di+∑j∈SONifi+fj+1difi=1di+∑j∈SONifi+fj+1dif_i=\frac{1}{d_i}+\sum_{j \in SON_i} \frac{f_i+f_j+1}{d_i}
移项,

fi(1−di−1di)=1di+∑j∈SONifj+1difi(1−di−1di)=1di+∑j∈SONifj+1di

f_i(1-\frac {d_i-1}{d_i})=\frac{1}{d_i}+\sum_{j \in SON_i} \frac{f_j+1}{d_i}

fidi=1di+∑j∈SONifj+1difidi=1di+∑j∈SONifj+1di

\frac {f_i}{d_i}=\frac{1}{d_i}+\sum_{j \in SON_i} \frac{f_j+1}{d_i}

fi=1+∑j∈SONi(fj+1)fi=1+∑j∈SONi(fj+1)

f_i=1+\sum_{j \in SON_i} (f_j+1)

fi=di+∑j∈SONifjfi=di+∑j∈SONifj

f_i=d_i+\sum_{j \in SON_i} f_j

这样我们就可以得到fff。
再来考虑gj" role="presentation">gjgjg_j
类似fifif_i,我们可以得到gi=1+1+gfa+gidfa+∑j∈SONfa,j≠ifj+1+gidfagi=1+1+gfa+gidfa+∑j∈SONfa,j≠ifj+1+gidfag_i=\frac {1+1+g_{fa}+g_i}{d_{fa}}+\sum_{j \in SON_{fa},j\neq i}\frac {f_j+1+g_i}{d_{fa}}。(其中fa表示i的父亲)
化简的过程也是和fifif_i相同的,化简后可得

gi=ffa+gfa−fi​gi=ffa+gfa−fi​

g_i=f_{fa}+g_{fa}-f_i​

是不是优美到自己都不敢相信,两条式子都是整数。
得到了f和gf和gf和g,对询问直接求lca即可。
需要注意的是,对于根节点的儿子的ggg,用上面的式子不能直接求。
和上文的方法相同,很容易得到gi=droot+∑j∈SONroot,j≠ifj(i∈SONroot)" role="presentation">gi=droot+∑j∈SONroot,j≠ifj(i∈SONroot)gi=droot+∑j∈SONroot,j≠ifj(i∈SONroot)g_i=d_{root}+\sum_{j \in SON_{root},j\neq i}f_j(i \in SON_{root})


code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1e5+10,mo=1e9+7;
int last[N],g[2][N],f[N][21][3],d[N],deep[N],n,m,D[N];
struct node{int a,b;
}a[N*2];
void dfs(int x,int y) {if (d[x]==1 && x!=1) {g[0][x]=1;return;}int s=0,i;for (i=last[x];i!=0;i=a[i].b){if (a[i].a!=y) {dfs(a[i].a,x);s=(s+g[0][a[i].a])%mo;}}g[0][x]=(d[x]+s)%mo;
}
void dfs1(int x,int y) {if (x!=1 && y!=1) g[1][x]=(g[0][y]+g[1][y]-g[0][x]+mo)%mo;for (int i=last[x];i!=0;i=a[i].b)if (a[i].a!=y) dfs1(a[i].a,x);
}
void dfs2(int x,int y) {if (x!=1) {f[x][0][2]=d[d[0]-1];f[x][0][0]=g[0][x];f[x][0][1]=g[1][x];}int i,k;for (i=1;(1<<i)<d[0];i++) {k=(1<<i);f[x][i][2]=d[d[0]-k];f[x][i][0]=(f[x][i-1][0]+f[f[x][i-1][2]][i-1][0])%mo;f[x][i][1]=(f[x][i-1][1]+f[f[x][i-1][2]][i-1][1])%mo;}for (int i=last[x];i!=0;i=a[i].b)if (a[i].a!=y) {deep[a[i].a]=deep[x]+1;d[++d[0]]=a[i].a; dfs2(a[i].a,x);d[0]--;}
}
int lca(int x,int y) {int i,z1=0,z2=1;if (deep[x]<deep[y]) {swap(x,y);swap(z1,z2);}int z=0;while (deep[x]!=deep[y]) {for (i=0;deep[f[x][i][2]]>=deep[y];i++);i--;z=(z+f[x][i][z1])%mo;x=f[x][i][2];}while (x!=y) {for (i=0;f[x][i][2]!=f[y][i][2];i++);if (i==0) {z=(z+f[x][i][z1]+f[y][i][z2])%mo;break;}i--;z=(z+f[x][i][z1]+f[y][i][z2])%mo;x=f[x][i][2];y=f[y][i][2];}return z;
}
void add(int x,int y) {a[++a[0].a].a=y;a[a[0].a].b=last[x];last[x]=a[0].a;
}
int main() {freopen("tree.in","r",stdin);freopen("tree.out","w",stdout);int i,j,k;scanf("%d%d",&n,&m);for (i=1;i<=n-1;i++) {int x,y;scanf("%d%d",&x,&y);add(x,y);add(y,x);d[x]++;d[y]++;}dfs(1,0);g[1][1]=0;int s=0;for (i=last[1];i!=0;i=a[i].b) s=(s+g[0][a[i].a])%mo;for (i=last[1];i!=0;i=a[i].b) g[1][a[i].a]=(d[1]+s-g[0][a[i].a]+mo)%mo;dfs1(1,0);deep[1]=1;d[0]=d[1]=1;dfs2(1,0);for (i=1;i<=m;i++) {int x,y;scanf("%d%d",&x,&y);printf("%d\n",lca(x,y));}
}

jzoj 5814. 【NOIP提高A组模拟2018.8.14】 树(期望)相关推荐

  1. jzoj5814 [NOIP提高A组模拟2018.8.14] 树 树形dp

    Description 梦游中的你来到了一棵 N 个节点的树上. 你一共做了 Q 个梦, 每个梦需要你从点 u 走到 点 v 之后才能苏醒, 由于你正在梦游, 所以每到一个节点后,你会在它连出去的边中 ...

  2. JZOJ 5814. 【NOIP提高A组模拟2018.8.14】 树

    梦游中的你来到了一棵 N 个节点的树上. 你一共做了 Q 个梦, 每个梦需要你从点 u 走到点 v 之后才能苏醒, 由于你正在梦游, 所以每到一个节点后,你会在它连出去的边中等概率地选择一条走过去, ...

  3. 5814. 【NOIP提高A组模拟2018.8.14】 树(期望 + 倍增)

    5814. [NOIP提高A组模拟2018.8.14] 树 Problem 给定一棵nnn个点的树,m" role="presentation">mmm次询问,每次 ...

  4. jzoj 5814. 【NOIP提高A组模拟2018.8.14】树 dp+lca

    Description 梦游中的你来到了一棵 N 个节点的树上. 你一共做了 Q 个梦, 每个梦需要你从点 u 走到 点 v 之后才能苏醒, 由于你正在梦游, 所以每到一个节点后,你会在它连出去的边中 ...

  5. 5814. 【NOIP提高A组模拟2018.8.14】 树

    题目描述 梦游中的你来到了一棵 N 个节点的树上. 你一共做了 Q 个梦, 每个梦需要你从点 u 走到 点 v 之后才能苏醒, 由于你正在梦游, 所以每到一个节点后,你会在它连出去的边中等概率地 选择 ...

  6. jzoj5814 【NOIP提高A组模拟2018.8.14】 树 (树上期望,递归法列方程)

    题面 梦游中的你来到了一棵 N 个节点的树上. 你一共做了 Q 个梦, 每个梦需要你从点 u 走到 点 v 之后才能苏醒, 由于你正在梦游, 所以每到一个节点后,你会在它连出去的边中等概率地 选择一条 ...

  7. JZOJ5814. 【NOIP提高A组模拟2018.8.14】 树

    Description 梦游中的你来到了一棵 N 个节点的树上. 你一共做了 Q 个梦, 每个梦需要你从点 u 走到 点 v 之后才能苏醒, 由于你正在梦游, 所以每到一个节点后,你会在它连出去的边中 ...

  8. JZOJ 5820. 【NOIP提高A组模拟2018.8.16】 非法输入

    Description 在算法竞赛中,题目一般保证了输入数据的合法性.然而在工程开发中,我们往往不期望程 序得到的输入都是合法的. D 君正忙着向校内 OJ 添加题目,在写了第 233 个 val.c ...

  9. 5817. 【NOIP提高A组模拟2018.8.15】 抄代码

    Description J 君是机房的红太阳,每次模拟她总是 AK 虐场.然而在 NOIP2117 中,居然出现了另一位 AK 的选手 C 君! 这引起了组委会的怀疑,组委会认为 C 君有抄袭 J 君 ...

最新文章

  1. java5引入包装类型的意义_Java中的基本类型和包装类
  2. 教你如何以对象的方式思考
  3. Flutter开发之JSON及序列化(29)
  4. electron/nodejs实现调用golang函数
  5. 安卓换语言java_Android应用程序的语言切换
  6. jquery实现截取pc图片_如何优雅的对网页截取长图
  7. 最新PC游戏下载链接
  8. 2018怎么更换图框_2018 乐博睿 全年回顾
  9. mysql i o error_警告:mysqli_ERROR()需要精确的一个参数,0给定的错误
  10. 矿业公司Aurum对其房东提起法律诉讼,要求收回矿机
  11. 九章基础算法04:二叉搜索树与哈希表
  12. C++中缀表达式求值(中缀表达式转后缀表达式)
  13. seata分布式事务原理_分布式事务 Seata 及其三种模式详解
  14. bondprice+matlab,债券久期与凸度的Matlab实现
  15. aligned_allocator
  16. 微信公众号行业排行榜周榜
  17. MySQL_MySQL配置文件
  18. wangEditor在IE中截图上传问题(一)
  19. google专利的脚步
  20. Linux下通过vmplayer安装CactiEZ

热门文章

  1. C#-Log4net 封装log类并自定义log存放路径
  2. CentOS 7 压缩包安装MySQL(教程+一键完成脚本)
  3. php removechild,介绍删除节点函数removeChild()用法
  4. php fopen函数php,php fopen函数用法(打开创建文件)_PHP教程 - fopen
  5. 如何让字体随窗口缩放自动调整大小
  6. B端产品经理-官网设计总结
  7. JS控制滚动条的位置 JS控制TextArea滚动条自动滚动到最下部
  8. java mouse_JAVA MouseEvent实现纪要
  9. uboot中挂载U盘,利用FAT文件系统读写U盘文件
  10. SQL查询众数与中位数