两个点最近的共有的祖先节点。
这种算法,emmmmm....可以把它理解为是暴力,即:
假设树上有两个点x,y,那么从x走到根节点,将路过的点都进行标记,再从y向上走到根节点,第一次遇到已经标记过的节点时,就找到了LCA(x,y)。
该算法是一种离线算法,即在一次操作中将问题全部解决。具体过程如下:
1.枚举与当前节点有边相连的所有点,如果该点未被访问过,则从该点开始,返回1。(即dfs遍历每一棵子树)
2.将该节点设为自己的父亲节点,若该点为叶子节点或子节点已全部枚举完毕,则寻找与它有关系的节点,若有与它有关系的点且那个点已经被标记过,则当前它们的父亲节点的编号就是它们的最近公共祖先。
3.将该节点与自己的父亲节点合并。
总复杂度为:O(n+q)
代码如下:
int d[N];
bool vis[N];
void Tarjan(int k) {
vis[k]=1;
for(int i=head[k];i;i=next[i]) {
if(vis[ver[i]]) continue;
Tarjan(ver[i]);
fa[ver[i]]=k;
}
for(int i=q_head[k];i;i=q_next[i])
if(vis[q_ver[i]])
ans[q_num[i]]=getfather(q_ver[i]);
}
HDU2586 How far away?
题目大意:
有一条n-1条边的树。有m个询问,分别求两点的距离。
分析:
用一个dist数组来表示到根节点的距离,则两点的距离即为dist[i]+dist[j]-2*dist[lca].
代码如下:
#include
#include
//杭电不支持万能头...
using namespace std;
#define N 200001
#define M 200001
int n,m,head[N],q_head[M],ver[N],q_ver[M],nxt[N],q_next[M],tot,q_num[N],q_tot,aska[N],askb[N],edge[N],T;
//杭电next为敏感词...
int fa[N],ans[M],dist[N];
bool vis[N];
inline void add(int x,int y,int z) {
nxt[++tot]=head[x];
head[x]=tot;
ver[tot]=y;
edge[tot]=z;
}
inline void add_(int x,int y,int number) {
q_next[++q_tot]=q_head[x];
q_num[q_tot]=number;
q_head[x]=q_tot;
q_ver[q_tot]=y;
}
inline void init() {
memset(q_head,0,sizeof(q_head));
memset(vis,0,sizeof(vis));
memset(head,0,sizeof(head));
memset(ans,0,sizeof(ans));
memset(dist,0,sizeof(dist));
memset(aska,0,sizeof(aska));
memset(askb,0,sizeof(askb));
tot=0,q_tot=0;
//初始化
scanf("%d%d",&n,&m);
for(int i=1;i