[LCA模版] Distance Queries

题目描述

约翰的奶牛们拒绝跑他的马拉松,因为她们悠闲的生活不能承受他选择的长长的赛道。因此他决心找一条更合理的赛道。此题的输入于第一题相同,紧接着下一行输入一个整数K,以后K行为K个"距离问题"。每个距离问题包括两个整数,就是约翰感兴趣的两个农场的编号,请你尽快算出这两地之间的距离。

N个点,N-1条边

 

输入

第1行:两个分开的整数:N和M; 
第2..M+1行:每行包括4个分开的内容,F1,F2,L,D分别描述两个农场的编号,道路的长度,F1到F2的方向(N,E,S,W)。
第2+M行:一个整数K。(1<=k<=10,000)    
第3+M..2+M+K:每行表示一个问题,包含两个整数代表两个农场。

 

输出

第1..K行:对应每个问题,输出单独的一个整数给出正确的距离。

 

样例输入

7 6 1 6 13 E 6 3 9 E 3 5 7 S 4 1 3 N 2 4 20 W 4 7 2 S 3 1 6 1 4 2 6

样例输出

13 3 36

提示

 

农场2 到6 有 20+3+13=36 的距离。
           F1 --- (13) ---- F6 --- (9) ----- F3
| |
(3) |
| (7)
F4 --- (20) -------- F2 |
| |
(2) F5
|
F7

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
  
int gi()
{
    int str=0;char ch=getchar();
    while(ch>'9' || ch<'0')ch=getchar();
    while(ch>='0' && ch<='9')
    {
        str=str*10+ch-'0';
        ch=getchar();
    }
    return str;
}
  
const int N=40005;const int K=10005;
int num=0;
int fa[N];int dis[N];
int head[40005];bool d[N];
int number=0;int ans[N];
int header[40005];
struct Lin
{
    int next,to,dis;
}a[N*2];
  
struct LIN
{
    int next,to,id;
}b[N*2];
  
void init(int x,int y,int z)
{
    a[++num].next=head[x];
    a[num].to=y;
    a[num].dis=z;
    head[x]=num;
}
  
void init2(int x,int y,int jk)
{
    b[++number].next=header[x];
    b[number].to=y;
    b[number].id=jk;
    header[x]=number;
}
  
int find(int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
  
void dfs(int x)
{
    d[x]=1;
    int u;int lca;int ppap;
    for(int i=header[x]; i ;i=b[i].next)
    {
        u=b[i].to;
        if(d[u])
        {
            ppap=b[i].id;
            lca=find(u);
            ans[ppap]=dis[u]+dis[x]-2*dis[lca];
        }
    }
     
    for(int i=head[x]; i ;i=a[i].next)
    {
        u=a[i].to;
        if(!d[u])
        {
            dis[u]=dis[x]+a[i].dis;
            dfs(u);
            fa[u]=x;
        }
    }
}
  
int main()
{
    int n,m;
    int x,y,z;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)fa[i]=i;
     
    while(m--)
    {
        x=gi();y=gi();z=gi();
        getchar();
        init(x,y,z);init(y,x,z);
    }
    int k=gi();
    for(int i=1;i<=k;i++)
    {
        x=gi();y=gi();
        init2(x,y,i);init2(y,x,i); 
    }
    dfs(1);
    for(int i=1;i<=k;i++)
        printf("%d\n",ans[i]);
    return 0;
}

 

转载于:https://www.cnblogs.com/Yuzao/p/6816845.html

### LCA(最近公共祖先)问题的模板代码与算法实现 对于解决LCA问题,倍增法是一种高效的方法。该方法通过预处理节点之间的关系来加速查询过程。 #### 倍增法求解LCA的核心思想 核心在于预先计算每个节点向上跳转$2^k$步后的父节点位置,并记录这些跳跃路径上的最值或和等信息以便后续快速查询[^1]。 #### 预处理阶段 在预处理过程中,构建稀疏表用于存储从任意一点出发经过若干次二进制幂次数目的上移操作所能到达的位置以及对应的最大最小边权或其他属性值。 ```cpp const int MAXN = 5e4 + 5; int dep[MAXN], fa[MAXN][20]; //dep[i]表示i结点到根的距离,fa[i][j]表示i结点往上第2^j个祖先是谁 vector<int> G[MAXN]; void dfs(int u,int father){ fa[u][0]=father; for(auto v : G[u]){ if(v==father) continue; dep[v]=dep[u]+1; dfs(v,u); } } //初始化f数组 for(int j=1;(1<<j)<=n;j++) for(int i=1;i<=n;i++) if(fa[i][j-1]!=-1) fa[i][j]=fa[fa[i][j-1]][j-1]; ``` #### 查询阶段 当需要找到两个给定节点u,v之间距离最近的那个共同祖先时,则可以通过比较两者深度并调整至相同层次再逐步回溯直至相遇于某一层级完成定位工作。 ```cpp int lca(int u,int v){ if(dep[u]<dep[v]) swap(u,v); //保证u更深或者一样深 for(int i=18;i>=0;i--) //先让较深的一个回到跟另一个同一层 if((dep[u]-dep[v])&(1<<i)) u=fa[u][i]; if(u==v) return u; //此时在同一层了 如果相等就返回 for(int i=18;i>=0;i--) if(fa[u][i]!=fa[v][i]){ u=fa[u][i]; v=fa[v][i]; } //一起往上面走直到第一次不一致的地方停下 return fa[u][0]; //最后一步走到lca处 } ``` 上述C++代码实现了基于DFS遍历树结构来进行倍增法预处理,并提供了`lca()`函数用来在线询问两节点间最低公共祖先的具体编号。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值