Codeforces 832D. Misha, Grisha and Underground【LCA】

本文介绍了一种在树形结构中进行路径查询的方法,重点在于计算三节点间的公共路径长度。通过使用LCA算法,实现了快速查找任意两点间最近公共祖先的功能,进而求解三节点间的最大公共路径问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:已知一棵树节点数n,n-1 条边组成。Q次询问,现在从中选取3个点a,b,c。 以一个点为顶点,另外两个点为起始点走最短路。要求得3种组合中,公共点数量最多的情况。


思路:枚举3种情况。

节点A,B之间最短路距离是:dis(A,B)=dep[A]-dep[LCA(A,B)]+dep[B]-dep[LCA(A,B)];

两节点A,B到节点C的    公共点个数=  (  dis(A,C)+dis(B,C)-dis(A,B)  )/2  + 1

接下来主要是模板一套就好了


数据分析:2 ≤ n ≤ 1051 ≤ q ≤ 105


复杂度分析:O(nlogn + q * logn)


CODE:(详见 算法学习之   LCA模板)传送门

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn=1e5+50;
const int maxlog=20;
int n,q,root=1;
int par[maxlog][maxn];
int dep[maxn];
vector <int> G[maxn];

void dfs(int v,int p,int d)
{
    par[0][v]=p;
    dep[v]=d;
    for(int i=0;i<G[v].size();i++)
        if(G[v][i]!=p)  dfs(G[v][i],v,d+1);
    return ;
}

void init()
{
    dfs(root,-1,0);
    for(int k=0;k<=maxlog-2;k++)
        for(int v=1;v<=n;v++)
            par[k+1][v]= par[k][v]<0 ? -1:par[k][par[k][v]];

    return ;
}

int lca(int u,int v)
{
    if(dep[u]>dep[v])   swap(u,v);
    for(int k=0;k<maxlog;k++)
        if((dep[v]-dep[u])>>k & 1)
            v=par[k][v];
    if(u==v)    return u;
    for(int k=maxlog-1;k>=0;k--)
        if(par[k][u]!=par[k][v])    u=par[k][u],v=par[k][v];
    return par[0][u];
}

int dis(int u,int v){return dep[u]+dep[v]-dep[lca(u,v)]*2;}

int cul(int t, int s ,int f){return (  dis(s,f)+dis(t,f)-dis(s,t)  )/2;}

int main(void)
{
    cin >> n >> q;
    for(int i=2;i<=n;i++)
    {
        int t;
        scanf("%d",&t);
        G[i].push_back(t);
        G[t].push_back(i);
    }
    init();
    while(q--)
    {

        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        printf("%d\n",max(  cul(a,b,c),max(cul(a,c,b) , cul(b,c,a)) )+1  );
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值