[LCA][数学]JZOJ 4794 富爷说是一棵树
分析
题意大概就是树上加一条边使一对点在一个简单环中的期望环长
我们发现,其实答案就是dist(u,v)*size[u]*size[v]+f[u]*size[v]+f[v]*size[u]
dist就是u到v的路径长度,size是子树大小,f是子树内所有点到子树的根的距离和
分母也显然是size[u]*size[v]
然后我们发现size和f都是可以预处理的,dist可以直接得出
然后如果u,v有祖孙关系的话,子树就不是往常意义的子树(假设v是祖先),那就是除了有u的那棵子树以外的都是v的子树
所以要处理一个g[v]来搞这种情况
#include <iostream> #include <cstdio> #include <memory.h> #include <cmath> using namespace std; typedef long long ll; const int N=1e5+10; struct Edge { int v,nx; }e[2*N]; int cnt,list[N]; int fa[N][20],dep[N]; ll f[N],g[N],sz[N]; int n,m,logn; void Add(int u,int v) { e[++cnt]=(Edge){v,list[u]};list[u]=cnt; e[++cnt]=(Edge){u,list[v]};list[v]=cnt; } void DFS(int u,int fat) { fa[u][0]=fat;dep[u]=dep[fat]+1;sz[u]=1; for (int i=list[u];i;i=e[i].nx) if (e[i].v!=fat) { DFS(e[i].v,u); sz[u]+=sz[e[i].v]; f[u]+=f[e[i].v]+sz[e[i].v]; } } void DFS_G(int u,int fa) { if (fa) g[u]=f[fa]-f[u]-sz[u]+g[fa]+(n-sz[fa]); for (int i=list[u];i;i=e[i].nx) if (e[i].v!=fa) DFS_G(e[i].v,u); } int LCA(int u,int v) { for (int i=logn;i>=0;i--) if (dep[fa[u][i]]>=dep[v]) u=fa[u][i]; if (u==v) return v; for (int i=logn;i>=0;i--) if (fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i]; return fa[u][0]; } int Witch_Son(int u,int v) { for (int i=logn;i>=0;i--) if (dep[fa[u][i]]>dep[v]) u=fa[u][i]; return u; } int main() { scanf("%d%d",&n,&m); for (int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),Add(u,v); DFS(1,0); DFS_G(1,0); logn=log(n)/log(2); for (int i=1;i<=logn;i++) for (int j=1;j<=n;j++) fa[j][i]=fa[fa[j][i-1]][i-1]; for (int i=1;i<=m;i++) { int u,v,lca,son; double ans=0,fm=0; scanf("%d%d",&u,&v); if (dep[u]<dep[v]) swap(u,v); lca=LCA(u,v); if (lca==v) { son=Witch_Son(u,v); ans=(double)(dep[u]-dep[v]+1)*sz[u]*(n-sz[son])+g[son]*sz[u]+f[u]*(n-sz[son]); fm=sz[u]*(n-sz[son]); } else { ans=(double)(dep[u]+dep[v]-2*dep[lca]+1)*sz[u]*sz[v]+f[u]*sz[v]+f[v]*sz[u]; fm=sz[u]*sz[v]; } printf("%lf\n",(double)ans/fm); } }
在日渐沉没的世界里,我发现了你。