首先一定需要学会倍增求解LCA倍增LCA讲解
树上前缀和: 利用一个数组sum 记录每个节点到根节点的路线上的权值和
有了这个sum数组我们可以得到任意两个节点u,v,他们到根节点路线上所有节点的权值和为sum[u]+sum[v] 这两条线是包含了两次从根节点到他们的最近公共祖先节点路线上的权值和,所以我们要减去两次sum[LCA(u,v)]在加上一个祖先节点的延迟就可以得出答案!
至于sum的求法,我们在dfs就深度的时候顺带利用如下公式
(假设u为v的前父亲节点)
sum[v]=sum[u]+val[v]
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e6+10;
int n,m,ecnt;
int head[maxn],depth[maxn],f[maxn][30],sum[maxn];
int du[maxn];
//链式前向星
struct edge {
int u,v,next;
} E[maxn];
void add(int u,int v) {
E[++ecnt].u=u;
E[ecnt].v=v;
E[ecnt].next=head[u];
head[u]=ecnt;
}
void dfs(int u,int d){
depth[u]=d;
for(int i=head[u];i;i=E[i].next){
int v=E[i].v;
if(!depth[v]){
sum[v]=sum[u]+du[v];//前缀和
f[v][0]=u;
dfs(v,d+1);
}
}
}
//LCA
int LCA(int x,int y){
if(depth[x]<depth[y])swap(x,y);
for(int i=20;i>=0;i--){
if(depth[x]-(1<<i)>=depth[y])x=f[x][i];
}
if(x==y)return x;
for(int i=20;i>=0;i--){
if(f[x][i]!=f[y][i]){
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
int main() {
cin>>n>>m;
int u,v;
for(int i=1;i<=n-1;i++){
cin>>u>>v;
add(u,v);
add(v,u);
du[u]++,du[v]++;
}
sum[1]=du[1];
dfs(1,1);
//预处理
for(int i=1;i<=20;i++){
for(int j=1;j<=n;j++){
f[j][i]=f[f[j][i-1]][i-1];
}
}
for(int i=1;i<=m;i++){
cin>>u>>v;
int pos=LCA(u,v);
cout<<sum[u]+sum[v]-2*sum[pos]+du[pos]<<endl;
}
return 0;
}