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.
题解
概率题,也就是套路嘛。
设
fi
f
i
表示从i走的
fai
f
a
i
的期望,
设
gi
g
i
表示从
fai
f
a
i
走的i的期望。
考虑
fi
f
i
的转移,
首先它肯能直接走到它父亲,或者先走到某个儿子,再回到i,在走到它父亲。
fi=(1+∑x∈sonifx+fi+1)
f
i
=
(
1
+
∑
x
∈
s
o
n
i
f
x
+
f
i
+
1
)
整理一下:
fi=∑x∈sonifx
f
i
=
∑
x
∈
s
o
n
i
f
x
gi
g
i
的转移类似,直接从父亲走过来,或者父亲走到另外的儿子,然后再回到父亲,再走到i,
gi=gfai+degi+∑x∈soni,x≠ifx
g
i
=
g
f
a
i
+
d
e
g
i
+
∑
x
∈
s
o
n
i
,
x
≠
i
f
x
直接bfs一次就可以求出f跟g了,
求一个前缀和,询问x到y的路径就是
x到lca,再从lca到y。
code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#define ll long long
#define N 100003
#define M 103
#define db double
#define P putchar
#define G getchar
using namespace std;
char ch;
void read(int &n)
{
n=0;
ch=G();
while((ch<'0' || ch>'9') && ch!='-')ch=G();
ll w=1;
if(ch=='-')w=-1,ch=G();
while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
n*=w;
}
int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}
const int mo=1000000007;
int n,m,x,y,z,d[N],ans;
int tot,lst[N],to[N*2],nxt[N*2];
int q[N],head,tail,fa[17][N];
int f[N],g[N],dep[N],sum;
void ins(int x,int y)
{
nxt[++tot]=lst[x];
to[tot]=y;
lst[x]=tot;
}
ll ksm(ll x,int y)
{
ll s=1;
for(;y;y>>=1,x=x*x%mo)
if(y&1)s=s*x%mo;
return s;
}
void add(int& x,int y)
{
x=x+y>mo?x+y-mo:x+y;
}
void bfs()
{
for(head=0,q[tail=1]=1;head<tail;)
{
x=q[++head];
for(int i=lst[x];i;i=nxt[i])
if(to[i]!=fa[0][x])
{
fa[0][to[i]]=x;
q[++tail]=to[i];
}
add(f[x],d[x]);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int j=16;j>=0;j--)
if(dep[fa[j][x]]>=dep[y])x=fa[j][x];
if(x==y)return x;
for(int j=16;j>=0;j--)
if(fa[j][x]!=fa[j][y])x=fa[j][x],y=fa[j][y];
return fa[0][x];
}
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
read(n);read(m);
for(int i=1;i<n;i++)
read(x),read(y),d[x]++,d[y]++,ins(x,y),ins(y,x);
bfs();
for(int i=n;i;i--)
for(int j=lst[q[i]];j;j=nxt[j])
if(to[j]!=fa[0][q[i]])add(f[q[i]],f[to[j]]);
f[1]=g[1]=0;
for(int i=1;i<=n;i++)
{
dep[q[i]]=dep[fa[0][q[i]]]+1;
sum=g[q[i]]+d[q[i]];
for(int j=lst[q[i]];j;j=nxt[j])
if(to[j]!=fa[0][q[i]])add(sum,f[to[j]]);
for(int j=lst[q[i]];j;j=nxt[j])
if(to[j]!=fa[0][q[i]])g[to[j]]=(sum-f[to[j]]+mo)%mo;
}
for(int i=1;i<=n;i++)
add(f[q[i]],f[fa[0][q[i]]]),
add(g[q[i]],g[fa[0][q[i]]]);
for(int j=1;j<17;j++)
for(int i=1;i<=n;i++)
fa[j][i]=fa[j-1][fa[j-1][i]];
for(int i=1;i<=m;i++)
{
read(x),read(y);
z=lca(x,y);ans=0;
add(ans,(f[x]-f[z]+mo)%mo);
add(ans,(g[y]-g[z]+mo)%mo);
printf("%d\n",ans);
}
return 0;
}