博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[洛谷P3379]【模板】最近公共祖先(LCA)
阅读量:4442 次
发布时间:2019-06-07

本文共 4764 字,大约阅读时间需要 15 分钟。

题目大意:给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

题解:有多种做法

 

倍增:(写于2017-9-13)

bfs函数:处理每个节点的深度(其实dfs更常用,当时我用的是bfs)和父亲(dad[i][0])

init函数:处理每个节点的二的整数幂的父亲,dad[i][j]存的是第i个节点的第$2^{j}$个祖先,dad[i][0]就是它的父亲,没有就是0。

可发现每个节点的第$2^{j}$个祖先是它的第$2^{j-1}$个祖先的$2^{j-1}$个祖先,也就是dad[i][j]=dad[dad[i][j-1]][j-1]($2^{n}$=$2^{n-1}$+$2^{n-1}$)。要特别注意一定要把关于j的循环放在外面,因为是一层一层处理的

LCA函数:求LCA,具体见程序中

 

C++ Code:

#include
#define maxn 500100struct Edge{ int next,to;}edge[maxn<<1];int dep[maxn],dad[maxn][20];int head[maxn],cnt;int q[maxn],v[maxn],t=0,w;int n,m,root;void swap(int &a,int &b){a^=b;b^=a;a^=b;}void add(int a,int b){ edge[++cnt].to=b; edge[cnt].next=head[a]; head[a]=cnt;}void bfs(){ v[q[w=1]=root]=dep[root]=1; while (t
=0;i--)if (dep[dad[x][i]]>=dep[y])x=dad[x][i]; */ if (x==y)return x;//如果两个已经相同就返回其中一个 for (int i=19;i>=0;i--){//只要dad[x][i]!=dad[y][i],两个节点一起向上跳(如果相同表示跳多了) if (dad[x][i]!=dad[y][i]){ x=dad[x][i]; y=dad[y][i]; } } return dad[x][0];//因为不相同,所以再向上跳一个 }int main(){ scanf("%d%d%d",&n,&m,&root); for (int i=1;i

 

 树链剖分:(写于2017-12-15)

dfs1,dfs2函数:树链剖分,见

LCA:求LCA,这一部分很简单,如果两个节点不在同一条重链上,就把其中重链顶部深度深的那个节点向上跳到其所在重链顶部的父亲。直到两个节点在同一条重链上,就返回深度较浅的一个

 

C++ Code:

#include
using namespace std;const int maxn=500100;int n,m,r,idx;int dep[maxn],fa[maxn],top[maxn],dfn[maxn],siz[maxn],son[maxn];int head[maxn],cnt;struct Edge{ int to,nxt;}e[maxn<<1];void swap(int &a,int &b){a^=b^=a^=b;}void addE(int a,int b){ e[++cnt]=(Edge){b,head[a]}; head[a]=cnt;}void dfs1(int rt){ siz[rt]=1; for (int i=head[rt];i;i=e[i].nxt){ int ne=e[i].to; if (ne!=fa[rt]){ fa[ne]=rt; dep[ne]=dep[rt]+1; dfs1(ne); if (son[rt]==0||siz[ne]>siz[son[rt]])son[rt]=ne; siz[rt]+=siz[ne]; } }}void dfs2(int rt){ dfn[rt]=++idx; int ne=son[rt]; if (ne)top[ne]=top[rt],dfs2(ne); for (int i=head[rt];i;i=e[i].nxt){ ne=e[i].to; if (ne==son[rt]||ne==fa[rt])continue; top[ne]=ne; dfs2(ne); }}int LCA(int x,int y){ while (top[x]!=top[y]){ if(dep[top[x]]

 

tarjan:(写于2018-8-5)

把询问离线,一次$dfs$即可,$O(n + m) $(有没有$\alpha$啊?)

 

C++ Code:

#include 
#define maxn 500100using namespace std;int n, m, root, x, y;int vis[maxn], f[maxn], ans[maxn];int head[maxn], cnt;struct Edge { int to, nxt;} e[maxn << 1];int headq[maxn], cntq;struct Query { int oth, nxt, tg;} Q[maxn << 1];void addE(int a, int b) { e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt;}void add(int a, int b, int c) { Q[++cntq] = (Query) {b, headq[a], c}; headq[a] = cntq;}int find(int x) {return ((x == f[x]) ? x : (f[x] = find(f[x])));}void dfs(int rt) { vis[rt] = true; int v; for (int i = head[rt]; i; i = e[i].nxt) { v = e[i].to; if (!vis[v]) { dfs(v); f[v] = find(rt); } } for (int i = headq[rt]; i; i = Q[i].nxt) ans[Q[i].tg] = find(Q[i].oth);}int main() { scanf("%d%d%d", &n, &m, &root); for (int i = 1; i < n; i++) { f[i] = i; scanf("%d%d", &x, &y); addE(x, y); addE(y, x); } f[n] = n; for (int i = 0; i < m; i++) { scanf("%d%d", &x, &y); add(x, y, i); add(y, x, i); } dfs(root); for (int i = 0; i < m; i++) { printf("%d\n", ans[i]); }}

  

RMQ:(写于2018-11-26)

$ST$表维护$RMQ$,先$dfs$,每次经过一个点就把这个点加入一个数组(可以证明,数组长度为$2n-1$),记$pos_i$为第$i$个点第一次在数组中出现的位置。求$x,y$的$LCA$,就是求区间$[pos_x,pos_y]$中深度最浅的点。$ST$表维护即可

卡点:$lg$数组没开两倍

 

C++ Code:

#include 
#include
#include
namespace __IO { namespace R { int x, ch; inline int read() { ch = getchar(); while (isspace(ch)) ch = getchar(); for (x = ch & 15, ch = getchar(); isdigit(ch); ch = getchar()) x = x * 10 + (ch & 15); return x; } }}using __IO::R::read;#define maxn 500010int head[maxn], cnt;struct Edge { int to, nxt;} e[maxn << 1];inline void addedge(int a, int b) { e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt; e[++cnt] = (Edge) {a, head[b]}; head[b] = cnt;}int n, m, rt;int LG[maxn << 1];#define M 20int ST[M][maxn << 1], dep[maxn], pos[maxn], idx;void dfs(int u, int fa = 0) { ST[0][++idx] = u, pos[u] = idx; for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (v != fa) { dep[v] = dep[u] + 1; dfs(v, u); ST[0][++idx] = u; } }}inline int getmin(int a, int b) {return dep[a] < dep[b] ? a : b;}inline int LCA(int x, int y) { int l = pos[x], r = pos[y]; if (l > r) std::swap(l, r); int tmp = LG[r - l + 1]; return getmin(ST[tmp][l], ST[tmp][r - (1 << tmp) + 1]);}int main() { n = read(), m = read(), rt = read(); for (int i = 1, a, b; i < n; i++) { a = read(), b = read(); addedge(a, b); } dfs(rt); for (int i = 1; i < 20; i++) { for (int j = 1; j + (1 << i) - 1 <= idx; j++) { ST[i][j] = getmin(ST[i - 1][j], ST[i - 1][j + (1 << i - 1)]); } } LG[0] = -1; for (int i = 1; i <= idx; i++) LG[i] = LG[i >> 1] + 1; while (m --> 0) { int x = read(), y = read(); printf("%d\n", LCA(x, y)); } return 0;}

  

 

转载于:https://www.cnblogs.com/Memory-of-winter/p/8046320.html

你可能感兴趣的文章
NameNode故障处理方法
查看>>
关于find的-perm
查看>>
修改pip默认安装目录
查看>>
[bzoj3073] Journeys 题解(线段树优化建图)
查看>>
vue中keepAlive的使用
查看>>
Oracle表空间、段、区和块简述
查看>>
Mysql数据库环境变量配置
查看>>
编程中经典语句
查看>>
bzoj3389
查看>>
生产环境 tomcat中启动缓慢
查看>>
【题解】Luogu P5290 [十二省联考2019]春节十二响
查看>>
linux中shell截取字符串方法总结
查看>>
BPM数据迁移
查看>>
简单工厂实现
查看>>
Android NDK JNI开发<6>
查看>>
Android Sqite数据库 <3>
查看>>
2017年需要学习和补充的专业技能
查看>>
Android类加载机制及热修复实现
查看>>
复利计算器5.0
查看>>
酒厂选址
查看>>