int pcnt = 0; // 用来计算遍历序 int first[MAXN]; int dfn[2 * MAXN]; // 注意数组大小 int deepth[2 * MAXN]; int dp[2 * MAXN][20]; voiddfs(int u, int fa, int dep){ dfn[++pcnt] = u; first[u] = pcnt; deepth[pcnt] = dep; for(int i = head[u]; i + 1; i = edge[i].nxt){ int v = edge[i].v; if(v == fa) continue; dfs(v, u, dep + 1); dfn[++pcnt] = u; deepth[pcnt] = dep; } } voidST(){ for(int i = 1; i <= pcnt; i++) dp[i][0] = i; for(int j = 1; j < 20; j++) for(int i = 1; i + (1 << j) - 1 <= pcnt; i++){ int a = dp[i][j - 1], b = dp[i + (1 << (j - 1))][j - 1]; if(deepth[a] < deepth[b]) dp[i][j] = a; else dp[i][j] = b; } } intRMQ(int l, int r){ int k = log((double)(r - l + 1)) / log(2.0); int a = dp[l][k], b = dp[r - (1 << k) + 1][k]; if(deepth[a] < deepth[b]) return a; elsereturn b; } intLCA(int L, int R){ int l = first[L]; int r = first[R]; if(l > r) swap(l, r); int pos = RMQ(l, r); return dfn[pos]; }
离线 TarJan 算法
简述
Tarjan算法(以发现者Robert Tarjan命名)是一个在图中寻找强连通分量的算法。算法的基本思想为:任选一结点开始进行深度优先搜索dfs(若深度优先搜索结束后仍有未访问的结点,则再从中任选一点再次进行)。搜索过程中已访问的结点不再访问。搜索树的若干子树构成了图的强连通分量。 应用到要解决的LCA问题上,则是:对于新搜索到的一个结点 u, 先创建由 u 构成的集合,再对 u 的每颗子树进行搜索,每搜索完一棵子树,这时候子树中所有的结点的最近公共祖先就是 u 了。
voidaddEdge(int u, int v){ edge[ecnt].v = v; edge[ecnt].nxt = head[u]; head[u] = ecnt++; }
voidinitfa(int root){ queue <int> que; que.push(root); depth[root] = 0; fa[root][0] = root; while(!que.empty()){ int u = que.front(); que.pop();
for(int i = 1; i < DEG; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1];
for(int i = head[u]; i + 1; i = edge[i].nxt){ int v = edge[i].v; if(v == fa[u][0]) continue; depth[v] = depth[u] + 1; fa[v][0] = u; que.push(v); } } }
intLCA(int u, int v){ if(depth[u] > depth[v]) swap(u, v); int du = depth[u], dv = depth[v]; int tu = u, tv = v; for(int det = dv - du, i = 0; det; det >>= 1, i++) if(det & 1) tv = fa[tv][i]; if(tu == tv) return tu; for(int i = DEG - 1; i >= 0; i--){ if(fa[tu][i] == fa[tv][i]) continue; tu = fa[tu][i]; tv = fa[tv][i]; } return fa[tu][0]; }
Gitalk 加载中 ...