LCA
以RMQ来实现
LCA(最近公共祖先)
用RMQ来实现,首先把树链哈希到链上。在链上用RMQ求最小值。
哈希这个过程可以用深搜,然后记录每个人节点出现的顺序,比如
对于下面这个图
通过深搜可以得到
然后我们用一个first数组来表示每个节点第一次出现的位置(即数组下标),比如我们找D和G的LCA,D第一次出现的位置为3,G第一次出现的位置为8,然后找他们的LCA就是找[3,8]这个区间的深度的最小值了。而这个树的节点的深度的区间最小值就可以用RMQ来实现了。
其中图的建立用邻接表
struct Edge{
int v,w;
Edge(int v_=0,int w_=0){
v=v_;
w=w_;
}
};
vector<Edge>G[maxn];
然后深搜过程为
void dfs(int u,int idx){
vis[u]=true;//标记是否访问过
point[++cnt]=u;//point表示节点的标号
first[u]=cnt;//first表示u这个节点第一次出现的位置
dep[cnt]=idx;//深度
for (int i=0;i<G[u].size();i++){
int v=G[u][i].v;
int w=G[u][i].w;
if (!vis[v]){
dis[v]=dis[u]+w;//dis[v]表示树根到达v的距离
dfs(v,idx+1);
point[++cnt]=u;
dep[cnt]=idx;
}
}
}
然后就是用RMQ预处理出任意两个位置之间的最小值(深度)
void RMQ_init(int n){
for (int i=1;i<=n;i++)dp[i][0]=i;
for (int k=1;(1<<k)<=n;k++){
for (int i=1;i+(1<<k)-1<=n;i++){
int a=dp[i][k-1];
int b=dp[i+(1<<(k-1))][k-1];
if (dep[a]>dep[b])dp[i][k]=b;
else dp[i][k]=a;
}
}
}
int RMQ(int l,int r){
int k=0;
while ((1<<(k+1))<=r-l+1)k++;
int a=dp[l][k];
int b=dp[r-(1<<k)+1][k];//保存的是编号
return dep[a]>dep[b]?b:a;
}
最后就是O(1)来得到LCA了
int LCA(int u,int v){
int x=first[u],y=first[v];
if (x>y)swap(x,y);
int ans=RMQ(x,y);
return point[ans];//返回编号所对应的节点。
}
基于倍增的方式
const int LOG = 20;//根据点数来取,取LOG >=logn就行了 20已经可以处理1e6以下的数据了
int par[N][LOG],dep[N];
void dfs(int u,int fa,int depth){
dep[u]=depth;
if(u==1){ //对于根来说,往上跳无论几下都是他自己。我习惯把1定为根所以写 u == 1
for(int i=0;i<LOG;i++) par[u][i]=u;
}else{
par[u][0]=fa; //往上跳2^0也就是1步显然是他的父亲
for(int i=1;i<LOG;i++) par[u][i]=par[par[u][i-1]][i-1];//类似RMQ的ST表的构造方法
}
for(int i=first[u];i!=-1;i=edge[i].next){
int v=edge[i].e;
if(v==fa) continue;
dfs(v,u,depth+1);
}
}
int up(int x,int step){ //返回x往上跳step步后的位置
for(int i=0;i<LOG;i++) if(step&(1<<i))
x=par[x][i];
return x;
}
int get_lca(int u,int v){
if(dep[u]<dep[v]) swap(u,v); //使得u为深度更大的点
u=up(u,dep[u]-dep[v]); //u往上跳dep[u] - dep[v]步使得u和v同等深度
if(u==v) return u; //u和v重合,LCA就是v
for(int i=LOG-1;i>=0;i--){ //同时跳
if(par[u][i]!=par[v][i]){
u=par[u][i];
v=par[v][i];
}
}
return par[u][0];
}
下面是一个LCA的板题,HDU 2586 传送门
代码:
RMQ转LCA
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#include <math.h>
#include <vector>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=4*1e4+10;
int n,m;
struct Edge{
int v,w;
Edge(int v_=0,int w_=0){
v=v_;
w=w_;
}
};
vector<Edge>G[maxn];
int dis[maxn];//dis[i]表示节点i到树根的距离
int dp[maxn*2][20];
int first[maxn];//first[i]表示i这个节点第一次出现的标号
int dep[maxn*2];//深度
int point[maxn*2];//point[i]表示标号为i所对应的节点
int vis[maxn];//标记节点是否被访问过了
int cnt;//遍历是节点的数目
void dfs(int u,int idx){//深搜
vis[u]=true;
point[++cnt]=u;
first[u]=cnt;
dep[cnt]=idx;
for (int i=0;i<G[u].size();i++){
int v=G[u][i].v;
int w=G[u][i].w;
if (!vis[v]){
dis[v]=dis[u]+w;
dfs(v,idx+1);
point[++cnt]=u;
dep[cnt]=idx;
}
}
}
void RMQ_init(int n){//预处理
for (int i=1;i<=n;i++)dp[i][0]=i;
for (int k=1;(1<<k)<=n;k++){
for (int i=1;i+(1<<k)-1<=n;i++){
int a=dp[i][k-1];
int b=dp[i+(1<<(k-1))][k-1];
if (dep[a]>dep[b])dp[i][k]=b;
else dp[i][k]=a;
}
}
}
int RMQ(int l,int r){//求最小值
int k=0;
while ((1<<(k+1))<=r-l+1)k++;
int a=dp[l][k];
int b=dp[r-(1<<k)+1][k];
return dep[a]>dep[b]?b:a;
}
int LCA(int u,int v){//求LCA
int x=first[u],y=first[v];
if (x>y)swap(x,y);
int ans=RMQ(x,y);
return point[ans];
}
int main()
{
int T;
scanf ("%d",&T);
while (T--){
memset (vis,0,sizeof (vis));
scanf ("%d%d",&n,&m);
for (int i=0;i<=n;i++)G[i].clear();
int u,v,w;
for (int i=0;i<n-1;i++){
scanf ("%d%d%d",&u,&v,&w);
G[u].push_back(Edge(v,w));
G[v].push_back(Edge(u,w));
}
dis[1]=0;cnt=0;
dfs(1,1);
RMQ_init(2*n-1);
while (m--){
scanf ("%d%d",&u,&v);
int lca=LCA(u,v);
int ans=dis[u]+dis[v]-2*dis[lca];
printf ("%d\n",ans);
}
}
return 0;
}
倍增的方式
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#include <math.h>
#include <vector>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=40000+10;
const int LOG=20;
struct Edge{
int v,w;
};
vector<Edge>G[maxn];
int par[maxn][LOG];
int dep[maxn];
int dis[maxn];
void dfs(int u,int fa,int depth){
dep[u]=depth;
if (u==1)for (int i=0;i<LOG;i++)par[u][i]=u;
else {
par[u][0]=fa;
for (int i=1;i<LOG;i++)par[u][i]=par[par[u][i-1]][i-1];
}
for (int i=0;i<G[u].size();i++){
int v=G[u][i].v;
if (v==fa)continue;
dis[v]=dis[u]+G[u][i].w;
dfs(v,u,depth+1);
}
}
int up(int x,int step){
for (int i=0;i<LOG;i++)if (step&(1<<i)){
x=par[x][i];
}
return x;
}
int get_lca(int u,int v){
if (dep[v]>dep[u])swap(u,v);
u=up(u,dep[u]-dep[v]);
if (u==v)return u;
for (int i=LOG-1;i>=0;i--){
if (par[u][i]!=par[v][i]){
u=par[u][i];
v=par[v][i];
}
}
return par[u][0];
}
int main()
{
int T;
scanf ("%d",&T);
int n,m;
while (T--){
scanf ("%d%d",&n,&m);
for (int i=0;i<=n;i++)G[i].clear();
int u,v,w;
for (int i=0;i<n-1;i++){
scanf ("%d%d%d",&u,&v,&w);
G[u].push_back(Edge{v,w});
G[v].push_back(Edge{u,w});
}
dfs(1,-1,1);
while (m--){
scanf ("%d%d",&u,&v);
int lca=get_lca(u,v);
printf ("%d\n",dis[u]+dis[v]-2*dis[lca]);
}
}
return 0;
}