LCA-最近公共祖先
LCA-最近公共祖先
性质:
-
d(u,v)=d(u)+d(v)-2d(LCA(u,v))(d(i)表示到编号为i节点的深度)
-
可以通过以上式子求两点之间的距离O(n)
\[LCA(S\cup S')=LCA(LCA(S),LCA(S')) \] -
LCA(u,v)必在u,v的最短路上
朴素法
int LCA(int x,int y)
{
if(d[x]<d[y])//d[x]表示x结点的深度
{ //此处表示将x表示较深的结点
int t=d[x];
d[x]=d[y];
d[y]=t;
}
while (d[x] != d[y])//此处表示将两者调整到同一层
{
x = father[x];
}
while(x!=y) //两点都向上搜索直到一致
{
x=father[x];
y=father[y];
}
return x;
}
倍增法
\[ f[x][i] = f[f[x][i - 1]][i - 1];
\]
其中x表示当前节点,i表示x前2^i个节点(父节点)
因为2i=2(i-1)+2^(i-1)
x前第2i个节点为:x的前第2(i-1)个节点的前第2^(i-1)节点
这样可以把第前第i个节点用i-1的递推式来表达
#include <bits/stdc++.h>
using namespace std;
vector<int> v[40005];
vector<long long> dis[40005];
int d[40005], f[40005][25];//防止超出后数组越界我们把数组开大点
long long l[40005];//此算法模板是设根节点深度为1情况下的模板,但是根的深度设置为0或1的区别不影响LCA结果
//O(nlogn)
void dfs(int x, int father)
{
d[x] = d[father] + 1;//每次搜索确定该节点的深度
for (int i = 1; (1 << i) <= d[x]; i++)//i=1是为了从i-1==0开始推导,每次进入一层新dfs前都会先初始化
{ //初始化f[x][0]
f[x][i] = f[f[x][i - 1]][i - 1];//从i-1==0开始递推,一直推到到2^i>d[x],即推出x的前第2^i个节点
//直到超出根节点的位置,那么超出不会越界或者错误吗?不会,推出的超出部分即比根节点还高的节点都为0(memset->0),我们设0为根节点的父节点
}
for (int i = 0; i < v[x].size(); i++)//开始遍历下一级的节点
{
int next = v[x][i];
if (next != father)
{
f[next][0] = x;//初始化下一级节点的上级节点为当前节点
l[next] = l[x] + dis[x][i];//通过当前节点推出下级节点到根节点的距离(是权值的和不是深度)
dfs(v[x][i], x);//深度优先搜索下一级节点
}
}
}
//O(logn)
int LCA2(int x, int y)
{
if (d[x] < d[y])
{
swap(x, y);//令x为较深节点
}
for (int i = 20; i >= 0; i--)//logn为i的初始值,可按照题意调整
{
if (d[f[x][i]] >= d[y])//当x的前第2^i个节点比y深时,将x点移动到改节点,因为1~2^n中的
{
x = f[x][i];
}
if (x == y)//该句只能放在上面那个if语句的下面,保证x变化后的值都会被判断,以防万一当i==0时x变化了但没有下一i此判断,然后x==y时不会进入上面那个语句,因为如果会进入上面的语句y!=x
return x;
}
//此处是从"远处"试探到"近处",x,y前2^i个节点不一致就跳到该这两个结点,
//求"最远"的,两节点不一致的结点,即,公共节点与非公共结点的边界
for (int i = 20; i >= 0; i--)//能用倍增法,且用一层循环就实现因为:1.每个正整数i(此处是第i个父节点)
{ //都能用2^x的和表示,2. 2的某个次方只出现一次(因为能出现两次以上就能用更大的数来表示,而能用更大数表示的在之前按已经循环到了3.且x,y跳跃后的值,再跳跃到非公共结点位置所需的i比从前一个结点跳到当前小,原因同上)
if (f[x][i] != f[y][i])
{
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];
}
int main()
{
int t;
int n, m;
scanf("%d", &t);
while (t--)
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n - 1; i++)//O(n)
{
int a, b;
long long k;
scanf("%d%d%lld", &a, &b, &k);
v[a].push_back(b);
v[b].push_back(a);
dis[a].push_back(k);
dis[b].push_back(k);
}
l[1] = 0;//初始化l[1],d[0]
d[0] = 0;
dfs(1, 0);
for (int i = 1; i <= m; i++)
{
int a, b;
scanf("%d%d", &a, &b);
int lca = LCA2(a, b);
// cout << lca << endl;
printf("%lld\n", l[a] + l[b] - 2 * l[lca]);
}
}
return 0;
}
本文来自博客园,作者:多巴胺不耐受仿生人,转载请注明原文链接:https://www.cnblogs.com/VoidCoderTF/articles/15435477.html

浙公网安备 33010602011771号