LCA

Posted czsharecode

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LCA相关的知识,希望对你有一定的参考价值。

Tarjan


#include<iostream>
#include<queue>
#include<list>
#include<vector>
#include<cstring>
#include<set>
#include<stack>
#include<map>
#include<cmath>
#include<algorithm>
#include<string>
#include<stdio.h>
using namespace std;
typedef long long ll;
#define MS(x,i) memset(x,i,sizeof(x))
#define rep(i,s,e) for(int i=s; i<=e; i++)
#define sc(a) scanf("%d",&a)
#define scl(a) scanf("%lld",&a)
#define sc2(a,b) scanf("%d %d", &a, &b)
#define debug printf("debug......\n");
#define pfd(x) printf("%d\n",x)
#define pfl(x) printf("%lld\n",x)
const double eps=1e-8;
const double PI = acos(-1.0);
const int inf = 0x3f3f3f3f;
const ll INF = 0x7fffffff;
const int maxn = 4e4+10;
const int M = 2e2+10;
int dx[4] = {0, 0, 1, -1};
int dy[4]  = {1, -1, 0 , 0};

int n,q;//n个顶点 q次询问
int lca[maxn];//lca[i]表示第i个询问的结果 祖先
int dist[maxn];//dist[i]表示i到根节点的距离

//对树建图
int head[maxn];
int cnt;
struct node{
    int v,nxt,w;
}edge[maxn*2];

//对询问建图
int headq[maxn];
int cntq;
struct Node{
    int u,v;//起点和终点
    int nxt;//同起点的下一个询问的位置
    int id;//记下是第几次询问
}query[M*2];

bool vis[maxn];//访问标志
//树加边
void addEdge(int u, int v, int w){
    edge[cnt].v = v;
    edge[cnt].w = w;
    edge[cnt].nxt = head[u];
    head[u] = cnt++;
}
//询问加边
void addQ(int u, int v, int idx){
    query[cntq].u = u;
    query[cntq].v = v;
    query[cntq].nxt = headq[u];
    query[cntq].id = idx;
    headq[u] = cntq++;
}
//并查集基本操作
int fa[maxn];
void init(){
    rep(i,1,n+5){
        fa[i] = i;
        vis[i] = 0;
        head[i] = 0;
        headq[i] = 0;
    } 
    dist[1] = 0;
    cnt = 1;
    cntq = 1;
}

int find(int x){
    if(x != fa[x]){
        return fa[x] = find(fa[x]);
    }
    return x;
}
void merge(int x, int y){
    int fx = find(x);
    int fy = find(y);
    if(fx != fy) fa[fy] = fx;
}

//tarjan
void tarjan(int u){
    vis[u] = 1;
    for(int i=head[u]; i; i=edge[i].nxt){
        int v = edge[i].v;
        int w = edge[i].w;
        if(!vis[v]){
            dist[v] = w + dist[u];//沿途求出每个结点到root的距离 root=1
            tarjan(v);
            merge(u , v);//v回溯,u是v的父亲
        }
    }
    //u结束 u回溯,把与u有查询关系的能更新的都更新了
    for(int i=headq[u]; i; i=query[i].nxt){
        int v = query[i].v;
        if(vis[v]) lca[query[i].id] = find(v);
    }
}

int t;
int main(){
    sc(t);
    while(t--){
        
        sc2(n,q);
        init();//初始化不能忘记
        int u,v,w;
        rep(i,1,n-1){
            sc2(u,v);
            sc(w);
            addEdge(u,v,w);
            addEdge(v,u,w);
        }
        rep(i,1,q){
            sc2(u,v);
            addQ(u,v,i);
            addQ(v,u,i);
        }
        tarjan(1);
        //事实上可以只选出奇数或者偶数次序的询问因为 (1,2) (3,4) (5,6)...括号内是等价的
        rep(i,1,cntq-1){
            i++;
            u = query[i].u;
            v = query[i].v;
            int idx = query[i].id;
            pfd(dist[u]+dist[v]-2*dist[lca[idx]]);
            
        }
    }


    return 0;
}

以上是关于LCA的主要内容,如果未能解决你的问题,请参考以下文章

代码源 Div1 - 105#451. Dis(倍增求LCA)

浅谈LCA

代码源 Div1 - 105#451. Dis(倍增求LCA)

LCA

二叉树---最近公共父节点(LCA)

LCA 最近公共祖先