题目中的树很大,不能直接建出。
但是题目中接树的次数较少,可以考虑把每棵树缩成一个点。
把每个接上的树进行标号,称为小树。
在倍增时距离的计算都计算的是小树的根的距离。
处理出倍增数组$f_{x,i}$表示$x$号小树向上跳$2^i$步后走到哪个点(被缩的),$d_{x,i}$表示$x$号小树向上跳$2^i$步后走了多远。和$dep_{x}$表示$x$号小树的深度。
可以直接按照定义计算。
接下来考虑询问。假设当前询问点是$a,b$且$dep_{a}>=dep_{b}$
特判掉询问点在同一棵小树内的情况。
分2种情况讨论:
1.$b$不是$a$的祖先。
设$lc=lca(a,b)$
将$a,b$跳到对应小树的根。
接下来把$a,b$跳到$lc$的下面一个点。
把$a,b$跳到分别的父亲。再统计$a,b$在$lc$的小树上的距离。
同时累加答案。
2.$b$是$a$的祖先
设$lc=lca(x,y)$
将$a$跳到对应小树的根。
接下来把$a,b$跳到$lc$的下面一个点。
把$a$跳到分别的父亲。再统计$a,b$在$lc$的小树上的距离。
同时累加答案。
我们在跳的过程中要求大树上的一个点$x$在原树上对应的点。假设$x$在编号为$y$的小树内部。
注意到$x$对应的小树在原树上的节点的编号是连续的,可持久化线段树合并后在$y$的线段树上二分即可。
思路较简单,但是代码很难写。
#include<bits/stdc++.h> using namespace std; #define int long long #define N 200010 int n,m,q,il[N],ss[N*30],ir[N],ct=1,sz[N],x[N],y[N],f[N][20],d[N][20],ff[N][20],gg[N][20],dd[N],dv[N],lc[N*30],rc[N*30],bg[N],mn[N],cc,rt[N],rr[N],rg[N]; vector<int>g[N],h[N]; int mg(int o,int p){ if(!o||!p)return o+p; int t=++cc; ss[t]=ss[p]+ss[o]; lc[t]=mg(lc[o],lc[p]); rc[t]=mg(rc[o],rc[p]); return t; } void mod(int &o,int l,int r,int x,int y){ if(!o)o=++cc; if(l==r){ ss[o]++; return; } int md=(l+r)/2; x<=md?mod(lc[o],l,md,x,y):mod(rc[o],md+1,r,x,y); ss[o]=ss[lc[o]]+ss[rc[o]]; } int qu(int o,int l,int r,int x){ if(l==r)return l; int md=(l+r)/2; if(x<=ss[lc[o]]) return qu(lc[o],l,md,x); return qu(rc[o],md+1,r,x-ss[lc[o]]); } int qs(int o,int l,int r,int x){ if(l==r)return ss[o]; int md=(l+r)/2; if(x<=md)return qs(lc[o],l,md,x); return qs(rc[o],md+1,r,x)+ss[lc[o]]; } void d1(int x,int fa){ mn[x]=x; sz[x]=1; ff[x][0]=fa; mod(rt[x],1,n,x,1); dd[x]=dd[fa]+1; for(int y:g[x]) if(y!=fa){ d1(y,x); rt[x]=mg(rt[x],rt[y]); mn[x]=min(mn[x],mn[y]); sz[x]+=sz[y]; } } int fd(int x){ int l=1,r=ct,ans; while(l<=r){ int md=(l+r)/2; if(il[md]<=x&&x<=ir[md])return md; if(x<il[md])r=md-1; else l=md+1; } } int lca(int x,int y){ if(dd[x]>dd[y])swap(x,y); for(int i=19;~i;i--) if(dd[ff[y][i]]>=dd[x]) y=ff[y][i]; if(x==y)return x; for(int i=19;~i;i--) if(ff[x][i]!=ff[y][i]) x=ff[x][i],y=ff[y][i]; return ff[y][0]; } int di(int x,int y){ int lc=lca(x,y); return dd[x]+dd[y]-2*dd[lc]; } int ll(int x,int y){ if(dv[x]>dv[y])swap(x,y); for(int i=19;~i;i--) if(dv[f[y][i]]>=dv[x]) y=f[y][i]; if(x==y)return x; for(int i=19;~i;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[y][0]; } int gt(int c,int e){ int a=fd(c),b=fd(e),ans=0; if(a==b){ int p1=qu(rt[rr[a]],1,n,-il[a]+c+1),p2=qu(rt[rr[b]],1,n,-il[b]+e+1); return di(p1,p2); } if(dv[a]<dv[b]){ swap(a,b); swap(c,e); } int lv=ll(a,b),ok=0,p1=qu(rt[rr[a]],1,n,-il[a]+c+1),p2=qu(rt[rr[b]],1,n,-il[b]+e+1); if(lv==b){ ans+=dd[p1]-dd[x[a]]; ok=1; c=rg[a]; } else{ ans+=dd[p1]-dd[x[a]]; ans+=dd[p2]-dd[x[b]]; c=rg[a]; e=rg[b]; } for(int i=19;~i;i--){ if(!ok&&dv[f[a][i]]>=dv[b]){ ans+=d[a][i]; c=rg[f[a][i]]; a=f[a][i]; } else if(ok&&dv[f[a][i]]>dv[b]){ ans+=d[a][i]; c=rg[f[a][i]]; a=f[a][i]; } } if(a==b){ ans+=di(x[c],x[e]); return ans; } if(ok){ int v1=qu(rt[x[f[a][0]]],1,n,y[a]-il[f[a][0]]+1),v2=qu(rt[rr[b]],1,n,e-il[b]+1); ans+=di(v1,v2); ans++; return ans; } for(int i=19;~i;i--) if(f[a][i]!=f[b][i]){ ans+=d[a][i]; ans+=d[b][i]; c=x[f[a][i]]; e=y[f[b][i]]; a=f[a][i],b=f[b][i]; } int v1=qu(rt[x[f[a][0]]],1,n,y[a]-il[f[a][0]]+1),v2=qu(rt[x[f[a][0]]],1,n,y[b]-il[f[b][0]]+1); ans+=2; ans+=di(v1,v2); return ans; } signed main(){ //freopen("r.in","r",stdin); cin>>n>>m>>q; for(int i=1;i<n;i++){ int x,y; cin>>x>>y; g[x].push_back(y); g[y].push_back(x); } d1(1,0); ff[1][0]=1; for(int i=1;i<20;i++) for(int j=1;j<=n;j++) ff[j][i]=ff[ff[j][i-1]][i-1]; il[1]=1; ir[1]=n; x[1]=1; rr[1]=1; for(int i=2;i<=m+1;i++){ cin>>x[i]>>y[i]; int p=fd(y[i]); ct++; il[ct]=ir[ct-1]+1; g[p].push_back(ct); g[ct].push_back(p); rg[ct]=qs(rt[x[i]],1,n,x[i])+il[ct]-1; f[ct][0]=p; ir[ct]=il[ct]+sz[x[i]]-1; rr[ct]=x[i]; } for(int i=2;i<=ct;i++){ int t=qu(rt[x[f[i][0]]],1,n,-il[f[i][0]]+y[i]+1); dv[i]=dv[f[i][0]]+1; bg[i]=mn[x[i]]; d[i][0]=di(t,x[f[i][0]])+1; } f[1][0]=1; for(int i=1;i<20;i++) for(int j=1;j<=ct;j++){ f[j][i]=f[f[j][i-1]][i-1]; d[j][i]=d[f[j][i-1]][i-1]+d[j][i-1]; } while(q--){ int x,y; cin>>x>>y; cout<<gt(x,y)<<' '; } }