关于LCA
题目链接:点这里
LCA最简单的思想就是暴力搜,如下:
对于每一组点u,v,求LCA(u,v)
先判断它们是否在同一深度上,不在则暴力将深度靠下的点暴力向上
然后再一同向上
但是对于上面的题一定会T了几个点
所以我们使用倍增
因为对于以上的暴力向上,我们实际上可以一次性向上走很多步,就像一步一步走和跨一大步一样
至于怎么跨一大步呢?
我们需要用到2进制了
因为对于每一个数,我们都可以用2进制表示
那么u到LCA(u,v)是不是也可以表示为2进制呢?
所以我们需要维护一个数组f[i][j]表示i节点向上走2^j次是哪一个节点
并且f[i][j] = f[f[i][j - 1]][j - 1](j == 0时f[i][j] = fa[i])
然后将他们递归到一个深度
先假设depth[u] > depth[v]
所以u要向上走k = depth[u] - depth[v]步
然后又因为k可以分解为2进制
所以你会发现u要向上走的步数从一步一步走变成了在二进制下k的每一位如果是1u = f[u][位数]
然后就是如何走到一处
我们从最大步数开始向下枚举(i)
如果f[u][i] != f[v][i]我们就让u,v分别向上走2 ^ i步
然后LCA(u,v) = f[u][0] = f[v][0]
这道题就解完了
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 100;
int n, q;
int dep[maxn];
long long dis[maxn];
int f[maxn][30];
struct Edge{
int to,weight;
};
vector<Edge> e[maxn];
void dfs(int u,int fa){
f[u][0] = fa;
for(int i = 1;i <= 20;i++){
f[u][i] = f[f[u][i - 1]][i - 1];
}
for(int i = e[u].size() - 1;i >= 0;i--){
int v = e[u][i].to;
int w = e[u][i].weight;
if(v == fa)continue;
dep[v] = dep[u] + 1;
dis[v] = dis[u] + w;
dfs(v,u);
}
}
int LCA(int u,int v){
if(dep[u] < dep[v])swap(u,v);
int k = dep[u] - dep[v];
for(int i = 20;i >= 0;i--){
if((k >> i) & 1){
u = f[u][i];
}
}
if(u == v)return u;
for(int i = 20;i >= 0;i--){
if(f[u][i] != f[v][i]){
u = f[u][i];
v = f[v][i];
}
}
return f[u][0];
}
int main(){
scanf("%d%d",&n,&q);
int u, v, w;
for(int i = 1;i < n;i++){
scanf("%d%d%d",&u,&v,&w);
e[u].push_back(Edge{v,w});
e[v].push_back(Edge{u,w});
}
dep[1] = 0;
dis[1] = 0;
dfs(1,0);
for(int i = 1;i <= q;i++){
scanf("%d%d",&u,&v);
printf("%d\n",dis[u] - 2 * dis[LCA(u,v)] + dis[v]);
}
return 0;
}