ST表递推公式:
st[i][j]=st[st[i][j−1]][j−1]st[i][j] = st[st[i][j-1]][j-1]st[i][j]=st[st[i][j−1]][j−1]
表示iii向上跳深度为2j2^j2j为iii向上跳深度为2j−12^{j-1}2j−1再向上跳深度为2j−12^{j-1}2j−1
树上距离公式:
dis[a,b]=dis[a]+dis[b]−2dis[lca(a,b)]dis[a,b] = dis[a]+dis[b]-2dis[lca(a,b)]dis[a,b]=dis[a]+dis[b]−2dis[lca(a,b)]
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 4e4+500;
int n,m,beg[maxn],tot,dep[maxn],fa[maxn],dis[maxn];
struct node{int to,nex,val;}edge[maxn*2];//链式前向星
int st[maxn][21];//ST表
inline void add(int u,int v,int val){
edge[++tot].to = v;
edge[tot].nex = beg[u];
edge[tot].val = val;
beg[u] = tot;
}
void dfs(int now,int f,int deep){
fa[now] = f , dep[now] = deep;
for(int i=beg[now];i;i=edge[i].nex){
int nx = edge[i].to;
if(nx==f) continue;
dis[nx] = dis[now]+edge[i].val;
dfs(nx,now,deep+1);
}
}
void init(){
for(int j=0;(1<<j)<=n;j++)
for(int i=1;i<=n;i++)
st[i][j] = j==0?fa[i]:-1;
for(int j=1;(1<<j)<=n;j++) //j表示向上跳2^j步
for(int i=1;i<=n;i++) //i表示节点编号
if(st[i][j-1]!=-1)
st[i][j] = st[st[i][j-1]][j-1];
}
int lca(int a,int b){
if(dep[a]<dep[b]) swap(a,b); //指定a节点为深度小的
int i,j;
for(i=0;(1<<i)<=dep[a];i++) ; //找到最大能跳多少
i--;
for(j=i;j>=0;j--) //跳到同样深度
if(dep[a]-(1<<j)>=dep[b])
a = st[a][j];
if(a==b) return a;
for(j=i;j>=0;j--){ //一起向上跳
if(st[a][j]!=-1&&st[a][j]!=st[b][j])
a = st[a][j] , b = st[b][j];
}
return fa[a]; //!一定会少跳一次
}
int main(){
int _;
scanf("%d",&_);
while(_--){
tot = 0;
for(int i=1;i<=n;i++)
beg[i]=0; //初始化
scanf("%d%d",&n,&m);
for(int i=1,u,v,val;i<=n-1;i++){
scanf("%d%d%d",&u,&v,&val);
add(u,v,val) , add(v,u,val);
}
dis[1] = 0; //指定1为根
dfs(1,0,1); //查询深度,父亲,树上距离
init(); //ST表初始化
while(m--){
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",dis[a]+dis[b]-2*dis[lca(a,b)]);//查询距离
}
}
return 0;
}