模板:LCA

LCA的模板

倍增算法实现

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define MAXN 50007
using namespace std;
vector <int> v[MAXN];
vector <int> w[MAXN];
//这里v用来存边
//这里w用来存每条边的权值
int fa[MAXN][31],cost[MAXN][31],dep[MAXN];
//fa 表示祖先  cost 表示花费  dep 表示深度
int n,m ;
int a,b,c;

void dfs(int root,int father){
    fa[root][0]=father;
    dep[root]=dep[fa[root][0]]+1;
    for(int i=1;i<31;i++){
        fa[root][i]=fa[fa[root][i-1]][i-1];//第2^i个祖先,是第2^i-1的祖先的2^i-1个祖先
        cost[root][i]=cost[fa[root][i-1]][i-1]+cost[root][i-1];
    }
    int sz=v[root].size();
    for(int i=0;i<sz;i++){
        if(v[root][i]==father)continue;
        cost[v[root][i]][0]=w[root][i];
        //v[root][i]指向root点的第i个子节点
        //cost[v[root][i]][0]表示root的第i个子节点到他的第一个父节点的cost花费
        //w[root][i]记载root到第i个节点的花费
        dfs(v[root][i],root);
    }
}
int ans=0;
int lca(int x,int y){
    if(dep[x]>dep[y]){
        swap(x,y);
    }
    int tmp =dep[y]-dep[x];
    for(int j=0;tmp;j++,tmp>>1){
        if(tmp&1){
            ans+=cost[y][j];
            y=fa[y][j];
        }
    }
    if(y==x){
        return ans;
    }
    //找到第一个不是他们祖先的两个点
    for(int j=30;j>=0&&y!=x;j--){
        if(fa[x][j]!=fa[y][j]){
            ans+=cost[x][j]+cost[y][j];
            x=fa[x][j];
            y=fa[y][j];
        }
    }
    ans+=cost[x][0]+cost[y][0];
    return ans;
}


signed main(){
     memset(fa ,0, sizeof(fa));
     memset(cost,0,sizeof(cost));
     memset(dep,0,sizeof(dep));
     scanf("%d",&n);
     for(int i=1;i<n;i++){
        scanf("%d %d %d",a,b,c);
        a++,b++;//调整点的序号,从1 开始计算
        v[a].push_back(b);
        v[b].push_back(a);//建立双向图
        w[a].push_back(c);
        w[b].push_back(c);//
     }
    dfs(1,0);
    cin>>m;
    for(int i=0;i<m;i++){
        scanf("%d %d",&a,&b);
        a++;
        b++;
        printf("%d\n",lca(a,b));
    }
    return 0; 
}

tarjan算法实现

/*
1.tarjan+并查 的主要思想在于对于一个公共节点,先探测完了左边
  在探测右边,必然会找到左边的值,此时返回的正是最近的公共节点
2.需要存边和查询操作
3.注意边的奇偶存法
*/
#include <algorithm>
#include <iostream>
using namespace std;

class Edge {
 public:
  int toVertex, fromVertex;
  int next;
  int LCA;
  Edge() : toVertex(-1), fromVertex(-1), next(-1), LCA(-1){};
  Edge(int u, int v, int n) : fromVertex(u), toVertex(v), next(n), LCA(-1){};
};

const int MAX = 100;
int head[MAX], queryHead[MAX];
Edge edge[MAX], queryEdge[MAX];//边和查询关系
int parent[MAX], visited[MAX];
int vertexCount, edgeCount, queryCount;

void init() {
  for (int i = 0; i <= vertexCount; i++) {
    parent[i] = i;
  }
}

int find(int x) {
    return parent[x]==x?x:find(x);
}

void tarjan(int u) {
  parent[u] = u;
  visited[u] = 1;

    for (int i = head[u]; i != -1; i = edge[i].next) {
    Edge& e = edge[i];
    if (!visited[e.toVertex]) {
      tarjan(e.toVertex);//搜索
      parent[e.toVertex] = u;//更新
    }
  }
    //当我们开始处理这一点的查询关系时,公共节点的左侧以及节点到
    //这里之前的点都已经被处理完成了
    //而且此时点已经并入了公共祖先
    for (int i = queryHead[u]; i != -1; i = queryEdge[i].next) {
    Edge& e = queryEdge[i];
    if (visited[e.toVertex]) {
      queryEdge[i ^ 1].LCA = e.LCA = find(e.toVertex); 
      //这里的1^i表示奇数则取偶,偶数则取奇,作用是翻转第一位
    }
  }
}

int main()
{
  memset(head, 0xff, sizeof(head));
  memset(queryHead, 0xff, sizeof(queryHead));

  cin >> vertexCount >> edgeCount >> queryCount;
  int count = 0;
  for (int i = 0; i < edgeCount; i++)
  {
    int start = 0, end = 0;
    cin >> start >> end;
    // 都采用奇偶存边的方法,,一条边存两遍
    // 可已通过异或的方法快速的得到另一条边
    edge[count] = Edge(start, end, head[start]);
    head[start] = count;
    count++;

    edge[count] = Edge(end, start, head[end]);
    head[end] = count;
    count++;
  }

  count = 0;
  for (int i = 0; i < queryCount; i++)
  {
    int start = 0, end = 0;
    cin >> start >> end;

    queryEdge[count] = Edge(start, end, queryHead[start]);
    queryHead[start] = count;
    count++;

    queryEdge[count] = Edge(end, start, queryHead[end]);
    queryHead[end] = count;
    count++;
  }

  init();
  tarjan(1);

  for (int i = 0; i < queryCount; i++)
  {
    Edge &e = queryEdge[i * 2];
    cout << "(" << e.fromVertex << "," << e.toVertex << ") " << e.LCA << endl;
  }

  return 0;
}

posted @ 2023-03-01 00:35  Aliancn  阅读(9)  评论(0编辑  收藏  举报