51nod-LCA问题_by_zyz

关于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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值