CodeForce 1304E 1-Trees and Queries【LCA】

本文探讨了在一个给定的树形结构中,如何高效地处理路径长度查询问题,尤其是在加入临时边后的路径长度变化。通过预处理和最近公共祖先(LCA)算法,文章提出了一种快速解决点间路径长度查询的方法,并考虑到路径长度的奇偶性,以判断是否存在特定长度的路径。

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

题意:

  首先给一棵 nnn 个点的树,然后有 qqq 次的询问,每次询问会向树上点 xxxyyy 之间增加一条边(保证增加的边之前不存在),然后问点 aaabbb 之间是否存在一条路径长度为 kkk 的路径。每次询问相互独立,即每次增加的边在下一次的询问中不存在。
数据范围:
3≤n≤105,1≤q≤105,1≤x,y,a,b≤n,x≠y,1≤k≤1093≤ n ≤10^5,1≤q≤10^5,1≤x,y,a,b≤n , x≠y, 1≤k≤10^93n105,1q105,1x,y,a,bn,x=y,1k109

思路:

  首先,在没有加边之前,假设点 aaabbb 最短路径为 len1len_1len1,那么,如果 k≥len1k\geq len_1klen1,并且 kkklen1len_1len1奇偶性相同,那么就可以通过重复走某些边来达到路径长度为 kkk
  加边后,我们考虑使用加的边是否可以使得点 a 和 b间的最短路径变短或者可以有一条路径长度与之前最短路径奇偶性不同的 “最短路径”(奇偶性不同情况下的最短)。
  把两种情况全部验证即可。

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int mak=20;
const int inf=0x3f3f3f3f;
vector<int>pic[N];
int depth[N],fa[mak][N];
void dfs(int v,int p,int d)
{
    depth[v]=d;
    fa[0][v]=p;
    for(int i=0;i<pic[v].size();i++)
    {
        int u=pic[v][i];
        if(u!=p)
            dfs(u,v,d+1);
    }
}
void init(int n)
{
    dfs(1,-1,0);
    for(int k=0;k+1<mak;k++)
    {
        for(int i=1;i<=n;i++)
        {
            if(fa[k][i]==-1)
                fa[k+1][i]=-1;
            else
                fa[k+1][i]=fa[k][fa[k][i]];
        }
    }
}
int lca(int u,int v)
{
    if(depth[u]>depth[v])
        swap(u,v);
    for(int k=0;k<mak;k++)
    {
        if((depth[v]-depth[u])>>k&1)
            v=fa[k][v];
    }
    if(u==v)
        return u;
    for(int k=mak-1;k>=0;k--)
    {
        if(fa[k][u]!=fa[k][v])
        {
            u=fa[k][u];
            v=fa[k][v];
        }
    }
    return fa[0][u];
}
int solve(int u,int v)
{
    int t=lca(u,v);
    return depth[u]+depth[v]-2*depth[t];
}
int main()
{
    int n,q,u,v,x,y,k;
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        pic[u].push_back(v);
        pic[v].push_back(u);
    }
    init(n);
    scanf("%d",&q);
    while(q--)
    {
        scanf("%d%d%d%d%d",&x,&y,&u,&v,&k);
        int ans=inf;//k=1e9,初值赋大点
        int tmp=solve(u,v);//原始树上两点间的最短距离
        int res=min(solve(u,x)+solve(v,y),solve(u,y)+solve(v,x))+1;//使用增加的边的最短距离
        //ans=min(ans,res);不能直接用二者中的最小值比较
        if(tmp%2==k%2)
            ans=tmp;
        if(res%2==k%2)
            ans=min(ans,res);
        printf("%s\n",ans<=k?"YES":"NO");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值