【模板】【图论】最近公共祖先(LCA)
2017-08-15 15:50
357 查看
1.树上倍增
时间复杂度O(nlogn)在线算法
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <cstdlib> #include <cmath> using namespace std; const int mxn =500003,mxm =500003; struct Edge{ int nx,to,val; }e[mxm*2]; int n,m,s,ecnt,depth; int hd[mxn],dep[mxn],f[mxn][25]; inline int read()//读入优化 { int x=0,sign=1; char c=' '; while(c<'0'||c>'9') { c=getchar(); if(c=='-') sign=-1; } while(c>='0'&&c<='9') { x=x*10+c-'0'; c=getchar(); } return sign*x; } inline void add_edge(int u,int v) { e[++ecnt].nx=hd[u]; hd[u]=ecnt; e[ecnt].to=v; } void dfs(int u) { for(int i=hd[u];i;i=e[i].nx) { int v=e[i].to; if(dep[v]) continue; dep[v]=dep[u]+1; f[v][0]=u; dfs(v); } } inline int LCA(int u,int v) { if(dep[u]>dep[v]) swap(u,v); int d=dep[v]-dep[u]; for(int i=0;i<=depth;i++) if(d&(1<<i)) v=f[v][i]; if(u==v) return u; for(int i=depth;i>=0;i--) { if(f[u][i]!=f[v][i]) { u=f[u][i]; v=f[v][i]; } } return f[u][0]; } int main() { n=read();m=read();s=read(); depth=log(n)/log(2)+1; for(int i=1;i<=n-1;i++) { int u,v; u=read();v=read(); add_edge(u,v); add_edge(v,u); } dep[s]=1; dfs(s); for(int j=1;j<=depth;j++) for(int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1]; for(int i=1;i<=m;i++) { int u,v; u=read();v=read(); printf("%d\n",LCA(u,v)); } return 0; }
2.树链剖分
时间复杂度O(nlogn)常数小
在线算法
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <cstdlib> #include <cmath> using namespace std; const int mxn =500003,mxm=500003; struct Edge{ int nx,to,val; }e[mxm*2]; int hd[mxn],fa[mxn],son[mxn],dep[mxn],top[mxn],siz[mxn],ecnt; int n,m,s; inline int read() { char c=' '; while(c<'0'||c>'9') c=getchar(); int x=0; while(c>='0'&&c<='9') { x=x*10+c-'0'; c=getchar(); } return x; } inline void add_edge(int u,int v) { e[++ecnt].nx=hd[u]; hd[u]=ecnt; e[ecnt].to=v; } inline void dfs1(int u)//求每个点的深度、子树大小和重儿子 { siz[u]=1; dep[u]=dep[fa[u]]+1; for(int i=hd[u];i;i=e[i].nx) { int v=e[i].to; if(fa[u]==v||fa[v]) continue; fa[v]=u; dfs1(v); siz[u]+=siz[v]; if(siz[v]>siz[son[u]]) son[u]=v; } } inline void dfs2(int u)//求每个节点的链顶 { if(u==son[fa[u]]) top[u]=top[fa[u]]; else top[u]=u; for(int i=hd[u];i;i=e[i].nx) { int v=e[i].to; if(fa[v]==u) dfs2(v); } } inline int LCA(int u,int v) { while(top[u]!=top[v])//不断把当前较深的节点跳到链顶,直到两个节点在一条链上 dep[top[u]]>dep[top[v]]?u=fa[top[u]]:v=fa[top[v]]; return dep[u]<dep[v]?u:v;//当两个节点在同一条链上时,深度较浅的就是LCA } int main() { n=read(); m=read(); s=read(); for(int i=1;i<=n-1;i++) { int u,v; u=read(); v=read(); add_edge(u,v); add_edge(v,u); } dep[s]=1; dfs1(s); dfs2(s); for(int i=1;i<=m;i++) { int u,v; u=read(); v=read(); printf("%d\n",LCA(u,v)); } return 0; }
3.tarjan算法
时间复杂度O(n)(线性复杂度诶)离线算法
跑的飞快
PS:Tarjan老爷子真是强,顺手膜一把
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <cstdlib> #include <cmath> using namespace std; const int mxn =500003,mxm =500003;//mxn代表最大节点数,mxm代表最大询问数 struct Edge{ int to,nx; }e[mxn*2];//邻接表存边 struct Query{ int to,nx,num; }q[mxm*2];//邻接表存储询问,num代表询问编号 bool vis[mxn];//vis数组代表该节点是否访问过 int hd[mxn],hq[mxm],f[mxn],ans[mxm];//hd和hq分别代表邻接表和询问的头指针 int ecnt=0,qcnt=0;//当前边编号,当前询问编号 inline int read()//读入优化 { char c=' '; while(c<'0'||c>'9') c=getchar(); int x=0; while(c>='0'&&c<='9') { x=x*10+c-'0'; c=getchar(); } return x; } int find(int x) { return f[x]==x?x:f[x]=find(f[x]); }//并查集 inline void add_edge(int u,int v)//加边 { e[++ecnt].nx=hd[u]; hd[u]=ecnt; e[ecnt].to=v; } inline void add_query(int u,int v,int num)//加询问 { q[++qcnt].nx=hq[u]; hq[u]=qcnt; q[qcnt].to=v; q[qcnt].num=num; } void LCA(int u,int fa)//对树进行dfs,递归求解LCA { vis[u]=true;//标记当前节点 for(int i=hd[u];i;i=e[i].nx)//以当前节点为根节点,寻找所有儿子 { int v=e[i].to; if(vis[v]||v==fa) continue; LCA(v,u); f[v]=u;//把v合并到当前根节点 } for(int i=hq[u];i;i=q[i].nx)//在遍历完当前节点u的子树后进行此操作 { int v=q[i].to; int num=q[i].num; if(vis[v]) ans[num]=find(v);//如果u没有被访问过,v被访问过,那么u和v的LCA就是find(v) } } int main() { int n,m,s; n=read();m=read();s=read(); for(int i=1;i<=n;i++) f[i]=i;//并查集初始化 for(int i=1;i<=n-1;i++) { int u,v; u=read();v=read(); add_edge(u,v); add_edge(v,u);//把树当成无向图处理 } for(int i=1;i<=m;i++)//离线处理,先把所有询问记下来 { int u,v; u=read();v=read(); add_query(u,v,i); add_query(v,u,i);//当询问u,v的LCA时,把u v和v u同时存储,便于更新答案 } LCA(s,0); for(int i=1;i<=m;i++) printf("%d\n",ans[i]);//按编号顺序输出答案 return 0; }
相关文章推荐
- 图论--最近公共祖先问题(LCA)模板
- 洛谷 P3379 【模板】最近公共祖先(LCA)
- 洛谷 P 3379 【模板】最近公共祖先(LCA)
- 4000 【模板】最近公共祖先(LCA)
- 洛谷P3379 【模板】最近公共祖先(LCA)(树链剖分)
- 【LCA倍增模板】【poj1330】最近公共祖先
- 【模板】最近公共祖先(LCA)
- ACM: 最近公共祖先问题LCA 图论题 …
- 洛谷P3379 【模板】最近公共祖先(LCA)
- tarjan离线算法-LCA最近公共祖先算法模板(详细)
- 【洛谷】3379 【模板】最近公共祖先(LCA)
- 树剖——【模板】最近公共祖先(LCA)
- 洛谷3379 【模板】最近公共祖先(LCA)
- LCA(最近公共祖先)模板
- AC日记——【模板】最近公共祖先(LCA)洛谷 P3379
- [模板]最近公共祖先LCA
- 洛谷P3379 【模板】最近公共祖先(LCA)
- P3379 【模板】最近公共祖先(LCA)
- P3379 【模板】最近公共祖先(LCA)
- 『图论』LCA 最近公共祖先