给定 n 和两个长度为 n 的正整数数列 a,b,以如下方式生成一棵树:
- u 的父亲 fau 在 [1,u−1] 中取,取到 i 的概率是 j=1∑u−1ajai;
- u 和 fau 之间有一条长度为 bu+bfau 的边。
q 组询问,每次给出两个点 x,y,求 x,y 之间的期望距离对 109+7 取模的结果。
2≤n,q≤3×105,1≤ai,bi≤3×103。
首先由于期望是线性的,所以设 leni 为 i 到 1 的期望距离,那么 x,y 之间的期望距离即为 lenx+leny−2lenlca(x,y)。
设 si=j=1∑iaj,leni 显然即为 j=1∑i−1si−1aj(lenj+bi+bj),可以前缀和快速求。
考虑求解 2lenlca(x,y),显然求 lenlca(x,y) 再乘上 2 即可。不妨钦定 x<y,那么一定有 lca(x,y)<x,考虑让 y 在树上不断往父亲跳,跳到 y′≤x 时停下。设 y′=i 的概率是 f(i),那么有 f(i)=sxai,归纳证明如下:
- 显然从 x+1 开始跳,跳一步就会停下,跳到 i 的概率是 sxai;
- 考虑从 k(k≥x+2)开始跳,设下一步跳到 p≤x 的概率是 w,跳到 p>x 的概率是 v。若 p≤x 则概率就是 w×sxap,否则由于 p 跳到 p′≤x 的概率是 sxap′,所以 p 跳到 p′≤x 的概率是 v×sxap′。那么最后跳到 p 的概率就是 w×sxap+v×sxap=sxap。
那么若最后 y 跳到了 x 则 lenlca(x,y) 就是 lenx,否则设最后跳到了 p 则变成求 lenlca(x,p),可以让 x 不断往父亲跳解决。所以设 dpx 表示 lenlca(x,y)(y>x)的期望值,那么有转移:
dpx=sxax×lenx+i=1∑x−1sxai×dpi
可以用前缀和快速求,那么做完了。
代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
#define fio(name) freopen(name".in","r",stdin),freopen(name".out","w",stdout)
const int S=300005,p=1000000007;
int n,q,a[S],b[S];
int sm[S],len[S],dp[S];
inline int qpow(int x,int y=p-2)
{
int res=1;
for(;y>0;y>>=1,x=1ll*x*x%p) res=(y&1)?1ll*res*x%p:res;
return res;
}
int main()
{
fio("name");
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
for(int i=1;i<=n;i++) sm[i]=(sm[i-1]+a[i])%p;
len[1]=0;
for(int i=2,val=1ll*a[1]*b[1]%p;i<=n;i++)
{
len[i]=(1ll*val*qpow(sm[i-1])%p+b[i])%p;
val=(val+1ll*a[i]*(len[i]+b[i])%p)%p;
}
dp[1]=0;
for(int i=2,val=0;i<=n;i++)
{
dp[i]=1ll*(val+1ll*a[i]*len[i]%p)*qpow(sm[i])%p;
val=(val+1ll*a[i]*dp[i]%p)%p;
}
while(q--)
{
int x,y;
scanf("%d%d",&x,&y);
if(x==y)
{
puts("0");
continue;
}
if(x>y) swap(x,y);
printf("%d\n",((len[x]+len[y])%p-2ll*dp[x]%p+p)%p);
}
return 0;
}