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
一开始的想法是直接求概率,然后在算期望次数,然而并不用这么麻烦。
我们设
表示节点
走到他的父亲的期望步数,
表示它的父亲走到它的期望步数。
先考虑如何求
。
对于叶子节点它的
。
那其他的节点怎么求?
设
表示节点i的度数。
对于节点i有
的概率一步走到它的父亲,还有
走到他的一个儿子j花费总共
(1是有i走的j的花费)步走到i的父亲。
那么我们得到
移项,
这样我们就可以得到 。
再来考虑
类似 ,我们可以得到 。(其中fa表示i的父亲)
化简的过程也是和 相同的,化简后可得
是不是优美到自己都不敢相信,两条式子都是整数。
得到了 ,对询问直接求lca即可。
需要注意的是,对于根节点的儿子的 ,用上面的式子不能直接求。
和上文的方法相同,很容易得到
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));
}
}