luoojP1252树上的最短路(LCA模板)

传送门

分析

首先这是一道模板题,强调这是一道模板题,但我在复习的时候还WA了4次QAQ;倍增lca显然就是再树上做倍增,要理解的话可以画一棵树看看规律;
思路就是先dfs出所有节点的父亲,边权和深度,在做一个dp,求出所有的1,2,8,……2^20,一般就是这个左右根据数据而定。
之后就是再树上lca,先将深度比较低的节点向上倍增然后直至深度相同,如果两点是同一节点那么就直接返回答案(其实就是两者在同一棵子树的一条分支上)如果在子树的左右分支那么还要一起向上倍增。
最后还有一点需要注意那就是如果最后的时候还有父亲一样的时候加上最后两条边权。详细见代码。

#include<bits/stdc++.h>
#define N 100005
using namespace std;
int head[N],Next[N<<1],val[N<<1],vet[N<<1],tot;
inline void add(int x,int y,int z){
    tot++;
    vet[tot]=y;
    val[tot]=z;
    Next[tot]=head[x];
    head[x]=tot;
}//边表
int dep[N],f[30][N],d[30][N]; 
inline void dfs(int u,int fa){
    for(int i=head[u];i;i=Next[i]){
        int v=vet[i];
        if(v==fa)continue;//注意防止死循环
        f[0][v]=u;//v点向上2^0层的节点编号
        d[0][v]=val[i];//v点向上2^0层的链条长度
        dep[v]=dep[u]+1;//距离根的深度
        dfs(v,u);
    }
}//搜出所有的初始值
int n,m,c;
inline int lca(int x,int y){
    if(dep[x]>dep[y])swap(x,y);//y的深度比较大
    int ans=0;
    for(int i=20;i>=0;i--)
        if(dep[f[i][y]]>=dep[x]){
            ans+=d[i][y];
            y=f[i][y];
    }//lca至x与y的深度相同
    if(x==y)return ans;//如果是同一条链上
    for(int i=20;i>=0;i--)
        if(f[i][y]!=f[i][x]){
            ans+=d[i][x]+d[i][y];
            y=f[i][y];
            x=f[i][x];
        }//倍增至里最早的公共路径的lca
        if(f[0][x]==f[0][y])ans+=d[0][x]+d[0][y];//加上所谓的最后两条边。
        return ans;
}
int main(){
    scanf("%d%d",&n,&m);    
    for(int i=1;i<=m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);add(y,x,z);
    }//连边
    dep[1]=1;dfs(1,-1);//初始化 
    for(int i=1;i<=20;i++)
        for(int j=1;j<=n;j++){
            f[i][j]=f[i-1][f[i-1][j]];//2^i=2^i-1+2^i-1;所以j向上2^i的节点就是j向上2^i-1的节点再向上2^i-1的节点
            d[i][j]=d[i-1][j]+d[i-1][f[i-1][j]];//同理上面的j向上2^i就等于j向上2^i-1的长度+f[i-1][j]向上2^i-1的长度
        }
    scanf("%d",&c);
    for(int i=1;i<=c;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%d\n",lca(a,b));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36316033/article/details/79889173
今日推荐