最近公共祖先Least Common Ancestors(lca)
之前某区域赛银牌题是这个相关,发现高中信息学省赛也有这个...又发现不是太难 可以写一发
最近公共祖先就是在一颗树中某2节点最近的公共祖先 字面意思
树的图论定义是任意两点有且仅有一条简单路 所以知道树中2点的lca就可以O(1)的算出最短路或其他相关数据 显然比直接用最短路算法快(hdu 2586)
但树和无向无环图是有区别的 无向无环图是双向边 可以以任一点为根 而树是有向边(x是y的父节点 不能颠倒)需要找出唯一存在的根(所以在poj1330直接套hdu2586的代码样例都过不了)
直接说算法好了..最高效的是tarjan算法(图论其他相关问题也会用到) 对于n个点m组询问 时空复杂度都是O(n+m) (并查集复杂度视为常数的话)这是复杂度下界 不可能被卡时间(所以对于无向无环图 n个点求q组最短路只要O(n+q)线性的时间)
算法特点是用链表一次存下所有边和所有询问 对根执行tarjan算法 执行同时得到每点对于这个根的深度(就是每个点到根的最短路)
写起来有点复杂 需要并查集 链表存边 和一些中间数组 当做模板好了
还有在线的倍增算法,复杂度为O((n+q)logn),但是是有必要掌握的,有的题目不可以离线算。这里就不介绍了..
以Hdu 2586为例 一种O(n+m)内存常数比较小的写法 可以当做模板:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define N 40005
#define M 202
struct { int to, w, ne; }e[N << 1];
struct { int to, ne; }qe[M << 1];
int qh[N], eh[N], ans[M], dis[N], pre[N], vis[N], et, qt;
//2个链表头 答案 到根的距离 并查集 vis数组
void init()
{
et = qt = 0, memset(vis, -1, sizeof(vis));
memset(eh, -1, sizeof(eh)), memset(qh, -1, sizeof(qh));
memset(ans, -1, sizeof(ans));
}
void add1(int u, int v, int w)
{
e[et].to = v, e[et].w = w, e[et].ne = eh[u], eh[u] = et++;
}
void add2(int u, int v)
{
qe[qt].to = v, qe[qt].ne = qh[u], qh[u] = qt++;
}
int find(int u)
{
if (pre[u] != u)
pre[u] = find(pre[u]);
return pre[u];
}
void lca(int u, int deep, int root)
{
int i, v;
pre[u] = u, dis[u] = deep, vis[u] = root;
for (i = eh[u]; ~i; i = e[i].ne)
{
v = e[i].to;
if (vis[v] == -1)
lca(v, deep + e[i].w, root), pre[v] = u;
}
for (i = qh[u]; ~i; i = qe[i].ne)
{
v = qe[i].to;
if (vis[v] == root)
ans[i / 2] = dis[v] + dis[u] - 2 * dis[find(v)];
//find(v)=lca(u,v)
}
}
int main()
{
int u, v, w, i, m, c;
scanf("%*d");
while (~scanf("%d%d", &m, &c))
{
init();
for (i = 1; i < m; i++)
scanf("%d%d%d", &u, &v, &w), add1(u, v, w), add1(v, u, w);
for (i = 0; i < c; i++)
scanf("%d%d", &u, &v), add2(u, v), add2(v, u);
lca(1, 0, 1);
for (i = 0; i < c; i++)
if (ans[i] == -1) puts("Not connected");
else printf("%d\n", ans[i]);
}
}
主要是分清树和图的区别 和看出表面无关的题目怎么和lca扯上的关系..
Poj 1330 树的lca 找到根执行tarjan算法就好 只有一组询问 复杂度O(n)
Hdu 2586图的lca 随便找一点执行lca u到v的最短路是uv到lca(u,v)距离的和减去2*根到lca(u,v)的距离
计蒜客 小X的佛光 noip题目 图的lca 推出O(1)的公式算出所有lca 处理输出就好
Poj 1986 据说是无环图所以直接lca就能过 和hdu2586一样 但感觉题目没明确说无环..
Hdu 2874 森林里找lca 稍微处理下就好 模板不好会卡内存...