D155 最近公共祖先 T72503 点的距离

D155 最近公共祖先 T72503 点的距离_哔哩哔哩_bilibili

 

T72503 点的距离 - 洛谷

给定一棵 n 个点的树,Q 个询问,每次询问点 x 到点 y 两点之间的距离。

思路

树上两点之间的距离,必然要经过它们的最近公共祖先 $lca$

周游一遍树,预处理出各点到根的距离 $d[i]$,各点的倍增祖先 $fa[i][j]$

每次询问 (x,y),可以 $d(x,y)=d[x]+d[y]-2*d[lca]$

相关板子:

D09【模板】最近公共祖先 倍增算法 - 董晓 - 博客园

 

// LCA 倍增算法 O(nlogn)
#include<bits/stdc++.h>
using namespace std;

const int N=100010;
int n,m;
vector<int> e[N];
int dep[N],fa[N][19],d[N];

void dfs(int u,int f){ //预处理dep,fa,d数组
  dep[u]=dep[f]+1; fa[u][0]=f;
  for(int i=1; i<=18; i++) fa[u][i]=fa[fa[u][i-1]][i-1];
  for(int v:e[u]){
    if(v==f) continue;
    d[v]=d[u]+1; //v到根的距离
    dfs(v,u);
  }
}
int lca(int x,int y){ //求LCA
  if(dep[x]<dep[y]) swap(x,y);
  for(int i=18; i>=0; i--)if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
  if(x==y) return x;
  for(int i=18; i>=0; i--)if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
  return fa[x][0];
}
int main(){
  scanf("%d",&n);
  for(int i=1,a,b; i<n; i++){
    scanf("%d%d",&a,&b);
    e[a].push_back(b);
    e[b].push_back(a);
  }
  dfs(1,0);
  scanf("%d",&m);
  for(int a,b;m--;){
    scanf("%d%d",&a,&b);
    printf("%d\n",d[a]+d[b]-d[lca(a,b)]*2);
  }
}

 

// 树链剖分  
#include<bits/stdc++.h>
using namespace std;

const int N=100010;
int n,m,a,b,c;
vector<int> e[N];
int dep[N],fa[N],son[N],sz[N],dis[N];
int top[N];

void dfs1(int u,int father){
  fa[u]=father,dep[u]=dep[father]+1,sz[u]=1;
  for(int v:e[u]){
    if(v==father) continue;
    dis[v]=dis[u]+1;
    dfs1(v,u);
    sz[u]+=sz[v];
    if(sz[son[u]]<sz[v])son[u]=v;
  }
}
void dfs2(int u,int t){
  top[u]=t;
  if(!son[u]) return;
  dfs2(son[u],t);
  for(int v:e[u]){
    if(v==fa[u]||v==son[u])continue;
    dfs2(v,v);
  }
}
int lca(int x,int y){
  while(top[x]!=top[y]){
    if(dep[top[x]]<dep[top[y]])swap(x,y);
    x=fa[top[x]];
  }
  return dep[x]<dep[y]?x:y;
}
int main(){
  scanf("%d",&n);
  for(int i=1; i<n; i++){
    scanf("%d%d",&a,&b);
    e[a].push_back(b);
    e[b].push_back(a);
  }
  dfs1(1,0);
  dfs2(1,1);
  scanf("%d",&m);
  while(m--){
    scanf("%d%d",&a,&b);
    int d=dis[a]+dis[b]-dis[lca(a,b)]*2;
    printf("%d\n",d);
  }
}

 

// Tarjan 算法 
#include<bits/stdc++.h>
using namespace std;

typedef pair<int,int> PII;
const int N=100010,M=N*2;
int n,m,a,b;
vector<int> e[N];
vector<PII> query[N];
int fa[N],vis[N],dis[N];
int ans[M];

int find(int u){
  if(fa[u]==u) return u;
  return fa[u]=find(fa[u]);
}
void tarjan(int u){
  vis[u]=1;
  for(int v:e[u]){
    if(vis[v]) continue;
    dis[v]=dis[u]+1;
    tarjan(v);
    fa[v]=u;
  }
  for(auto ed:query[u]){
    int v=ed.first,i=ed.second;
    if(vis[v])
      ans[i]=dis[u]+dis[v]-dis[find(v)]*2;
  }    
}
int main(){
  scanf("%d",&n);
  for(int i=1; i<n; i++){
    scanf("%d%d",&a,&b);
    e[a].push_back(b);
    e[b].push_back(a);
  }
  scanf("%d",&m);
  for(int i=1; i<=m; i++){
    scanf("%d%d",&a,&b);
    query[a].push_back({b,i});
    query[b].push_back({a,i});
  }
  for(int i=1;i<=n;i++) fa[i]=i;
  tarjan(1);
  for(int i=1; i<=m; i++)
    printf("%d\n",ans[i]);
}

  

posted @ 2026-05-09 10:54  董晓  阅读(55)  评论(0)    收藏  举报