题目大意
给出一棵 (n) 个节点的树。有 (m) 个询问,每一个询问包含两个数 (a,b)。
我们可以对任意两个不相连的点连一条无向边,并且使得加上这条边后 (a,b) 处在一个环内。
对于每一个询问,求这样的环的期望长度。
(2leq n,mleq 10^5)
题解
树形dp+二次扫描换根。
设询问 (u,v) 这两个点,(usim v) 的这条树链把树分为三部分:
- 从 (u) 出发,不经过 (usim v) 这条树链,所能到达的点集,记为 (A)。
- 从 (v) 出发,不经过 (usim v) 这条树链,所能到达的点集,记为 (B)。
- 剩下的点构成的点集,记为 (C)。
记 (Sum) 表示新增一条跨越 (usim v) 这条链的边构成的所有环的长度之和,计 (Num) 表示新增一条跨越 (usim v) 这条链的边的方案数。则 (Ans=frac{Sum}{Num})。
[Sum=sum_{ain A}sum_{bin B}(dis(a,u)+dis(v,b)+dis(u,v)+1)\
=(dis(u,v)+1) imes|A||B|+sum_{ain A}sum_{bin B}(dis(a,u)+dis(v,b))\
=(dis(u,v)+1) imes|A||B|+sum_{ain A}sum_{bin B}dis(a,u)+sum_{ain A}sum_{bin B}dis(v,b))\
=(dis(u,v)+1) imes|A||B|+|B| imessum_{ain A} dis(a,u)+|A| imessum_{bin B} dis(v,b)\
]
[Num=|A| imes|B|
]
不妨以(1)为根,令 (Deep[u]<Deep[v]),令
[dp[u][0]=sum_{vin subtree(u)} dis(u,v)\
dp[u][1]=sum_{v
otin subtree(u)} dis(u,v)
]
即 (dp[u][0]) 表示从 (u) 出发向下走的所有路径的长度和,(dp[u][1]) 表示从 (u) 出发向上走的所有路径的长度和,设 (fa) 是 (u) 的父亲,那么有
[dp[u][0]=sum_{vin son(u)}dp[v][0]+Size[v] \
dp[u][1]=dp[fa][1]+dp[fa][0]-dp[u][0]-Size[u]+N-Size[u]
]
我们可以先通过一次DFS求得 (dp[u][0]),再通过一次DFS扫描换根求得 (dp[u][1]),然后就容易求得 (Sum)。
对于 (lca(u,v)=u) 和 (lca(u,v)
eq u),稍有不同,需要分类讨论。
时间复杂度 (O(nlog n))。
Code
#include <bits/stdc++.h>
using namespace std;
#define RG register int
#define LL long long
template<typename elemType>
inline void Read(elemType &T){
elemType X=0,w=0; char ch=0;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
T=(w?-X:X);
}
struct Graph{
struct edge{int Next,to;};
edge G[200010];
int head[100010];
int cnt;
Graph():cnt(2){}
void clear(int node_num=0){
cnt=2;
if(node_num==0) memset(head,0,sizeof(head));
else fill(head,head+node_num+5,0);
}
void add_edge(int u,int v){
G[cnt].to=v;
G[cnt].Next=head[u];
head[u]=cnt++;
}
};
Graph G;
int Size[100010],Deep[100010],Anc[100010][18];
LL dp[100010][2];
int N,M;
void DFS(int u,int fa){
Size[u]=1;
Anc[u][0]=fa;
for(int i=1;i<18;++i)
Anc[u][i]=Anc[Anc[u][i-1]][i-1];
for(int i=G.head[u];i;i=G.G[i].Next){
int v=G.G[i].to;
if(v==fa) continue;
Deep[v]=Deep[u]+1;
DFS(v,u);
Size[u]+=Size[v];
dp[u][0]+=dp[v][0]+Size[v];
}
return;
}
void DFSB(int u,int fa){
if(fa) dp[u][1]=dp[fa][1]+dp[fa][0]-dp[u][0]-Size[u]+N-Size[u];
for(int i=G.head[u];i;i=G.G[i].Next){
int v=G.G[i].to;
if(v==fa) continue;
DFSB(v,u);
}
return;
}
int LCA(int u,int v){
int Root=1;
if(u==Root || v==Root) return Root;
if(Deep[u]<Deep[v]) swap(u,v);
for(RG i=17;i>=0;--i){
if(Deep[Anc[u][i]]>=Deep[v])
u=Anc[u][i];
}
if(u==v) return u;
for(RG i=17;i>=0;--i){
if(Anc[u][i]!=Anc[v][i]){
u=Anc[u][i];
v=Anc[v][i];
}
}
return Anc[u][0];
}
int main(){
Read(N);Read(M);
for(RG i=1;i<=N-1;++i){
int u,v;
Read(u);Read(v);
G.add_edge(u,v);
G.add_edge(v,u);
}
Deep[1]=1;
DFS(1,0);
DFSB(1,0);
for(RG i=1;i<=M;++i){
int u,v;
Read(u);Read(v);
if(Deep[u]>Deep[v]) swap(u,v);
int c=LCA(u,v);
if(c==u){
int s=v;
for(RG j=17;j>=0;--j)
if(Deep[Anc[s][j]]>=Deep[u]+1)
s=Anc[s][j];
LL temp=dp[u][1]+dp[u][0]-dp[s][0]-Size[s];
LL sum=(1LL+Deep[v]-Deep[u])*(LL)Size[v]*(LL)(N-Size[s])+(LL)Size[v]*temp+(LL)(N-Size[s])*dp[v][0];
double Ans=(double)sum/((double)Size[v]*(double)(N-Size[s]));
printf("%.8lf
",Ans);
}else{
LL sum=(1LL+Deep[v]+Deep[u]-2*Deep[c])*(LL)Size[v]*(LL)Size[u]+
(LL)Size[v]*dp[u][0]+(LL)Size[u]*dp[v][0];
double Ans=(double)sum/((double)Size[v]*(double)Size[u]);
printf("%.8lf
",Ans);
}
}
return 0;
}