题意
给定一棵
n
n
个节点的树, 个询问,每一个询问包含两个参数
a,b
a
,
b
,我们能够通过加一条边使
a,b
a
,
b
处于同一个环内。对于每一个询问,求这样的环的期望长度。
1≤n,m≤105
1
≤
n
,
m
≤
10
5
思路
细节比较多的一道题。首先我们可以用期望的线性性质,把环的期望拆成多个值,假设边连接
x,y
x
,
y
,形成
x−a−b−y−x
x
−
a
−
b
−
y
−
x
这样的环。那么
E[环]=E[(x,a)]+E[(y,b)]+(a,b)+1
E
[
环
]
=
E
[
(
x
,
a
)
]
+
E
[
(
y
,
b
)
]
+
(
a
,
b
)
+
1
。
不难发现,需要分类讨论,分
x,y
x
,
y
有无祖宗关系。
假如没有祖宗关系,那
x,y
x
,
y
只能分别在
a,b
a
,
b
的子树中出现,我们可以维护一个
sum1
s
u
m
1
数组,
sum1u
s
u
m
1
u
表示
u
u
的子节点到 的距离总和。
E[(x,a)]=sum1asza,E[(y,b)]=sum1bszb
E
[
(
x
,
a
)
]
=
s
u
m
1
a
s
z
a
,
E
[
(
y
,
b
)
]
=
s
u
m
1
b
s
z
b
而若有祖宗关系,假设
a
a
节点更深,那么仍有 ,而
y
y
则可以选择除 (设它为
z
z
) 这棵子树外的所有节点,那我们方便起见,可以先预处理一个节点到其它所有节点的距离之和 。
E[(y,b)]
E
[
(
y
,
b
)
]
只用将
sum2b
s
u
m
2
b
减去
(sum1z+szz)
(
s
u
m
1
z
+
s
z
z
)
, 即
b
b
到子树 中节点的距离总和,再除以
n−szz
n
−
s
z
z
即可。
代码
#include<bits/stdc++.h>
#define FOR(i,x,y) for(register int i=(x);i<=(y);++i)
#define DOR(i,x,y) for(register int i=(x);i>=(y);--i)
#define N 100003
typedef long long LL;
using namespace std;
template<const int maxn,const int maxm>struct Linked_list
{
int head[maxn],to[maxm],nxt[maxm],tot;
void clear(){memset(head,-1,sizeof(head));tot=0;}
void add(int u,int v){to[++tot]=v,nxt[tot]=head[u],head[u]=tot;}
#define EOR(i,G,u) for(register int i=G.head[u];~i;i=G.nxt[i])
};
Linked_list<N,N<<1>G;
int dfn[N],dep[N],fa[N],sz[N],son[N],top[N],ori[N],ord;
LL sum1[N],sum2[N];
int n,m;
LL gcd(LL x,LL y){return y?gcd(y,x%y):x;}
void dfs(int u,int f,int d)
{
dep[u]=d,fa[u]=f,sz[u]=1,son[u]=0,sum1[u]=0;
EOR(i,G,u)
{
int v=G.to[i];
if(v==f)continue;
dfs(v,u,d+1);
sum1[u]+=sum1[v]+sz[v];
sz[u]+=sz[v];
if(sz[v]>sz[son[u]])son[u]=v;
}
}
void make_path(int u,int f,int tp)
{
dfn[u]=++ord,ori[ord]=u,top[u]=tp;
if(son[u])make_path(son[u],u,tp);
EOR(i,G,u)
{
int v=G.to[i];
if(v==f||v==son[u])continue;
make_path(v,u,v);
}
}
int jmp(int x,int d)
{
while(dep[x]-dep[fa[top[x]]]<=d)
{
d-=dep[x]-dep[fa[top[x]]];
x=fa[top[x]];
}
return ori[dfn[x]-d];
}
int LCA(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]>dep[top[y]])x=fa[top[x]];
else y=fa[top[y]];
}
return dep[x]<dep[y]?x:y;
}
void redfs(int u,int f,LL cur)
{
sum2[u]=cur;
EOR(i,G,u)
{
int v=G.to[i];
if(v==f)continue;
redfs(v,u,cur-sz[v]+(n-sz[v]));
}
}
int main()
{
G.clear();
scanf("%d%d",&n,&m);
FOR(i,1,n-1)
{
int u,v;
scanf("%d%d",&u,&v);
G.add(u,v),G.add(v,u);
}
dfs(1,0,1);
ord=0;make_path(1,0,1);
redfs(1,0,sum1[1]);
while(m--)
{
int x,y,z,lca;
LL p1,p2,q1,q2,p,q;
scanf("%d%d",&x,&y);
if(dep[x]<dep[y])swap(x,y);
lca=LCA(x,y);
p1=sum1[x],q1=sz[x];
if(lca==y)
{
z=jmp(x,dep[x]-dep[y]-1);
p2=sum2[y]-sum1[z]-sz[z],q2=n-sz[z];
}
else p2=sum1[y],q2=sz[y];
p=q1*q2*(dep[x]+dep[y]-2*dep[lca]+1)+p1*q2+p2*q1;
q=q1*q2;
printf("%lld/%lld\n",p/gcd(p,q),q/gcd(p,q));
}
return 0;
}