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;
}