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;}
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 来月经发黑又少怎么办 月经血发黑量少怎么办 做人流后肚子胀怎么办 怀孕见红了肚子不痛怎么办 月经来是黑色的怎么办 怀孕了长了痔疮怎么办 怀孕了有外痔疮怎么办 孕妇长痔疮很痛怎么办 孕9个月尿路感染怎么办 旁边有人尿不出来怎么办 外阴破皮了应该怎么办 脸上长脂肪粒怎么办怎么能消除 挤黑头留下的坑怎么办 长痘留下的坑怎么办 鼻子上留下黑印怎么办 狗狗眼里长息肉怎么办 狗狗眼角长息肉怎么办 脸上长了好多脂肪粒怎么办 脸上毛孔粗大有黑头怎么办 脸颊毛孔粗有黑头怎么办 鼻子上有黑头怎么办小窍门 脸上很多粉刺和油脂粒怎么办 毛孔里都是角栓怎么办 脸上长了很多脂肪粒怎么办 脸上全是油脂粒怎么办 外阴口长了疙瘩怎么办 外阴痒怎么办用什么洗 有子宫内膜增厚怎么办 脸上痘痘特别疼怎么办 眉间和下巴长痘怎么办 眼下方两边长斑怎么办 眼睛下面长斑了怎么办 24岁眼下有细纹怎么办 17岁眼下有细纹怎么办 眼下有细纹怎么办 知乎 22岁眼部有细纹怎么办 20岁眼角有细纹怎么办 20岁眼睛有细纹怎么办 29岁眼下有细纹怎么办 招不干净的东西怎么办 伤口旁边肿了怎么办啊