LCA(Tarjan/RMQ)
来源:互联网 发布:系统分析师 知乎 编辑:程序博客网 时间:2024/06/03 16:00
- Tarjan算法实质上在树中使用一个未完全建好的并查集来寻找最近公共祖先(该并查集具有祖先节点为该子树树根的性质,由dfs逆序建立并查集得到),查询时必须使查询点中的一个已经在并查集中而另一个不在。这样如果两个点在同一树枝(从树根到叶子的一条路径)上时下方的节点的祖先即是上方节点,而如果两个点不在同一树枝上时,已在并查集中的点的祖先会是 包含两个询问点的最小子树 的树根,也就是最近公共祖先。
- 使用类似图的方法来储存查询,以提高访问效率
- 两个询问点之间的距离=a询问点到树根的距离+b询问点到树根的距离
−2× a,b的最近公共祖先到树根的距离
#include <cstdio>#include <iostream>#include <algorithm>#include <cstring>#include <vector>using namespace std;typedef long long LL;const int maxn=40005;int n,m;struct Edge{ int to,w;};bool vis[maxn];int sum[maxn];int f[maxn],ans[205],z[205],x[205];vector<Edge> G[maxn];vector<Edge> Q[maxn];void add_edge(int u,int v,int w){ G[u].push_back((Edge){v,w}); G[v].push_back((Edge){u,w});}int _find(int x){ return f[x]!=x?f[x]=_find(f[x]):f[x];}void _merge(int u,int v){ int fu=_find(u),fv=_find(v); if(fu!=fv){ f[fv]=fu; }}void dfs(int u,int f){ for(int i=0;i<G[u].size();++i){ Edge &cur=G[u][i]; if(vis[cur.to])continue; vis[cur.to]=1; sum[cur.to]=sum[u]+cur.w; dfs(cur.to,u); } for(int i=0;i<Q[u].size();++i){ Edge &cur=Q[u][i]; if(!vis[cur.to])continue; ans[cur.w]=_find(cur.to); //cout<<"f "<<ans[cur.w]<<endl; } _merge(f,u);}int main(){ int T; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); int a,b,c; for(int i=0;i<=n;++i)f[i]=i,Q[i].clear(),G[i].clear(); memset(vis,0,sizeof(vis)); sum[1]=0; //debug(f,5); for(int i=1;i<n;++i){ scanf("%d%d%d",&a,&b,&c); add_edge(a,b,c); } for(int i=0;i<m;++i){ scanf("%d%d",&a,&b); z[i]=a; x[i]=b; Q[a].push_back((Edge){b,i}); Q[b].push_back((Edge){a,i}); } vis[1]=1; dfs(1,1); //debug(sum,10); //debug(f,10); for(int i=0;i<m;++i){ cout<<sum[z[i]]+sum[x[i]]-2*sum[ans[i]]<<endl; } }}
RMQ在线查询也可以解决这个问题
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>#include <vector>#include <queue>#include <string>using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const double eps=1e-8;const int N=40005;int dep[N<<1];///记录欧拉序列每个位置的深度int fin[N];///树中每个点第一次访问时的idxint p[N<<1];///欧拉序列int dp[N<<1][20];///ST表int cnt;///idx///p[]、dep[]以及dp的第一维记得开点数两倍。///因为这三个数组的操作对象都是欧拉序列,而欧拉序列长度为2*n-1int n,m;int dist[N];struct Edge{ int to,w;};vector<Edge> G[N];void add_edge(int u,int v,int w){ G[u].push_back((Edge){v,w}); G[v].push_back((Edge){u,w});}int min(int i,int j){ ///重写min函数,因为ST表内只保存标号 return dep[i]<dep[j]?i:j;}void make_rmq(int x){ ///ST表构造 for(int i=0;i<=x;i++) dp[i][0]=i; ///长度为1的区间 for(int j=1;(1<<j)<=x;j++) for(int i=1;i+(1<<j)-1<x;i++) dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);}int query(int l,int r){ int k=(int)(log((r-l+1)*1.0)/log(2.0)); return min(dp[l][k],dp[r-(1<<k)+1][k]);}int get_lca(int u,int v){ if(fin[u]>fin[v]) return p[query(fin[v],fin[u])]; return p[query(fin[u],fin[v])];}void dfs(int u,int fa,int depth){ dep[cnt]=depth; p[cnt]=u; fin[u]=cnt++; for(int i=0;i<G[u].size();++i){ int v=G[u][i].to; if(v==fa) continue; dist[v]=dist[u]+G[u][i].w; dfs(v,u,depth+1); p[cnt]=u; dep[cnt++]=depth; }}int main(){ int T; scanf("%d",&T); while(~scanf("%d%d",&n,&m)){ cnt=0; for(int i=0;i<=n;++i){ G[i].clear(); } memset(dist,0,sizeof(dist)); int a,b,c; for(int i=1;i<n;++i){ scanf("%d%d%d",&a,&b,&c); add_edge(a,b,c); } dfs(1,-1,1); make_rmq(2*n-1); for(int i=0;i<m;++i){ scanf("%d%d",&a,&b); //cout<<dist[a]<<' '<<dist[b]<<endl; printf("%d\n",dist[a]+dist[b]-2*dist[get_lca(a,b)]); } } return 0;}
阅读全文
0 0
- LCA(Tarjan/RMQ)
- poj 1986 tarjan/rmq(LCA问题)
- LCA最近公共祖先(RMQ、Tarjan)
- LCA求法--Tarjan与倍增与RMQ
- poj 1330 Nearest Common Ancestors LCA tarjan/RMQ ST
- 最近公共祖先LCA模板(Tarjan/RMQ)
- LCA,RMQ,并查集, tarjan算法等相关
- lca&rmq
- 【RMQ & LCA】
- RMQ && LCA
- LCA+RMQ
- LCA && RMQ
- LCA+RMQ
- LCA&RMQ
- LCA&RMQ
- LCA&&RMQ
- Tarjan LCA
- Tarjan lca
- Nginx+uWSGI+Django Centos 部署
- Collections排序sort排序list多条件排序
- linux awk命令详解
- K&R之宏替换的例子
- 第四章 xadmin.views.base
- LCA(Tarjan/RMQ)
- 代码判断linux网卡是否是promisc模式
- sharesdk 3.x版本(即官方说的新版)使用步骤
- 使用w3c解析xml文档
- Python3之元类
- 1、KNN 学习笔记
- 关于虚拟现实(VR)内容开发综述
- 浅析ZigBee的应用层开发之终端结点
- openwrt --mt7688 nfs实现文件共享