ST——LCA在线算法
【LCA练习】最近公共祖先(版本1)
Description
给你一棵有根树,要求你计算出m对结点的最近公共祖先。
Input
输入文件的第一行包含两个整数n和m(2<=n,m<=200,000),其中n为结点个数,结点编号为1到n;m表示询问次数。
接下来n-1行,每行两个整数x和y,表示结点x是结点y的父亲结点;
接下来的m行,每行两个整数a和b,要求计算出结点a和b的最近公共 祖先。
Output
输出文件共m行,分别为每对结点的最近公共祖先编号。
Sample Input
5 3
2 3
3 4
3 1
1 5
3 5
4 5
1 2
Sample Output 3
3
2
思想:
转化为类似于RMQ的问题来解决
算法步骤:
- 读入
- 求出每一个节点的深度
- 开一个数组p[i][k]表示,从i节点向上的2的k次方的祖先是哪一个节点
- 初始化,动态规划求出p[i][k]
- 在线解决每一对问题,把a节点和b节点调平,按照最大的k,递减向上回溯,直到a和b的上方一个节点就是共同祖先
伪代码:
#include<bits/stdc++.h>
using namespace std;
struct 前向星
{
}a[100000];
int h[300000];
int cnt;
int n,m;
int prt[300000];
int root;
int d[300000];
int p[300000][20];
void Union(int x,int y)
{
连接x,y节点
}
void getdeep(int v0,deep)
{
从根节点递归求出每一个节点的深度
}
int deal(int x,int y)//在线ST算法
{
1.使d[x]>d[y],便于操作
2.求出最大的k(最大向上扩展高度<=2的k次方)
3.调平x,y节点
4.特判
5.以k的递减,x,y同时向上爬,如果。。。。。。
6.返回p[x][0];
}
int main()
{
cin>>n>>m;
循环读入
找根节点
递归深度
*初始化
DP求出p[i][k];
对每一对问题在线求解
}
是时候用真正的代码压压惊了
#include<bits/stdc++.h>
using namespace std;
struct eg
{
int next,to;
}a[300000];
int h[300000];
int cnt;
int n,m;
int prt[300000];
int root;
int d[300000];
int p[300000][20];
void Union(int x,int y)
{
cnt++;
a[cnt].to=y;
a[cnt].next=h[x];
h[x]=cnt;
}
void getdeep(int v0,int deep)
{
d[v0]=deep;
for(int i=h[v0];i;i=a[i].next)
{
getdeep(a[i].to,deep+1);
}
}
int deal(int x,int y)
{
if(d[x]<d[y])swap(x,y);
int key=log(d[x])/log(2);
for(int i=key;i>=0;i--)
{
if(d[x]-(1<<i)>=d[y])x=p[x][i];
}
if(x==y)return y;
for(int i=key;i>=0;i--)
{
if(p[x][i]!=p[y][i])x=p[x][i],y=p[y][i];
}
return p[x][0];
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n-1;i++)
{
int tis,tit;
scanf("%d%d",&tis,&tit);
Union(tis,tit);
prt[tit]=tis;
}
int root=1;
while(prt[root])root=prt[root];
getdeep(root,1);
for(int i=1;i<=n;i++)
{
p[i][0]=prt[i];
}
for(int k=1;(1<<k)<=n;k++)
{
for(int i=1;i<=n;i++)
{
p[i][k]=p[p[i][k-1]][k-1];
}
}
for(int i=1;i<=m;i++)
{
int tis,tit;
scanf("%d%d",&tis,&tit);
printf("%d\n",deal(tis,tit));
}
}