Codeforces 832D Misha, Grisha and Underground

本文介绍了一种解决特定树形结构中路径查询问题的方法,通过计算最近公共祖先(LCA)来找到使三条路径重合顶点最多的点。文章详细解释了如何使用深度优先搜索(DFS)和跳跃指针技巧预处理树结构,以便快速查找LCA,并给出了完整的代码实现。

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

转载请注明出处,谢谢http://blog.youkuaiyun.com/bigtiao097?viewmode=contents

题意

给一棵有n个节点的树,然后给q个询问,每次给3个点,可以任选一点为w,使得u,v到w两条路径所重合的顶点数最多;
(2n1e51q1e5)

思路

假设节点1为树的根
对于每个询问给定的三个点u、v、w,先两两求出LCA(最近公共祖先),记为a、b、c,再求出三个点的LCA,记为x

  • a=b=c=x,这时求x到u,v,w的最大值即可
  • a、b、c中有一个不等于x,不妨设是a,a = lca(u,v)
    此时求 (a到x与x到w之和)、(a到u)、(a到v )这三者的最大值

只可能有上述两种情况,自己画下图理解一下就好
求路径长度时直接利用求lca时计算出来的depth数组就行


具体代码如下:
Result:Accepted    Memory:19276 KB   Time : 358ms

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxv = 1e5+5;
const int maxl = 20;//log(maxv)
vector<int> G[maxv];
int root,n,q,x;
int s,f,t;
int a,b,c;
int ans;
int parent[maxv][maxl];
int depth[maxv];
void dfs(int v,int p,int d)
{
    parent[v][0] = p;
    depth[v] = d;
    for(int i=0;i<G[v].size();i++)
        if(G[v][i]!=p)
            dfs(G[v][i],v,d+1);
}
void init()
{
    memset(parent,-1,sizeof parent);
    dfs(root,-1,0);
    for(int k=1;(1<<k)<=n;k++)
        for(int v=1;v<=n;v++)
        {
            if(parent[v][k-1]<0)  parent[v][k]=-1;
            else    parent[v][k]=parent[parent[v][k-1]][k-1];
        }
}
int lca(int u,int v)
{
    if(depth[u]>depth[v])   swap(u,v);
    for(int k=0;k<maxl;k++)
        if(((depth[v]-depth[u])>>k)&1)
            v=parent[v][k];
    if(u==v)    return u;
    for(int k=maxl-1;k>=0;k--)
        if(parent[u][k]!=parent[v][k])
        {
            u = parent[u][k];
            v = parent[v][k];
        }
    return parent[u][0];
}
int main()
{
    cin>>n>>q;
    for(int i=2;i<=n;i++)
    {
        scanf("%d",&x);
        G[i].push_back(x);
        G[x].push_back(i);
    }
    root = 1;
    init();
    while(q--)
    {
        ans = 0;
        scanf("%d%d%d",&s,&f,&t);
        a = lca(s,f);
        b = lca(s,t);
        c = lca(t,f);
        x = lca(c,s);
        if(a==x&&b==x&&c==x)
        {
            ans = max(ans,abs(depth[x]-depth[s])+1);
            ans = max(ans,abs(depth[x]-depth[t])+1);
            ans = max(ans,abs(depth[x]-depth[f])+1);
        }
        else if(a!=x)
        {
            ans = max(ans,abs(depth[x]-depth[a])+abs(depth[x]-depth[t])+1);
            ans = max(ans,abs(depth[a]-depth[s])+1);
            ans = max(ans,abs(depth[a]-depth[f])+1);
        }
        else if(b!=x)
        {
            ans = max(ans,abs(depth[x]-depth[b])+abs(depth[x]-depth[f])+1);
            ans = max(ans,abs(depth[b]-depth[s])+1);
            ans = max(ans,abs(depth[b]-depth[t])+1);
        }
        else if(c!=x)
        {
            ans = max(ans,abs(depth[x]-depth[c])+abs(depth[x]-depth[s])+1);
            ans = max(ans,abs(depth[c]-depth[t])+1);
            ans = max(ans,abs(depth[c]-depth[f])+1);
        }
        printf("%d\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值