LCA进阶-卡内存版

博客围绕给定的有根树LCA问题展开,该问题有节点数、询问组数限制,且空间仅8M。常规LCA解法因空间限制不可行,提出模仿倍增LCA,让点维护向上跳√N个点的位置,还通过将dep和to信息合并解决数组空间不足问题。

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

题目描述

给定一棵以11号节点为根,由N个节点构成的有根树, 共QQ组询问, 每次询问两点间的LCA

2N1000000,1Q500002≤N≤1000000,1≤Q≤50000,时间限制1000ms1000ms, 空间限制8M8M

输入输出格式

输入格式

第一行两个个正整数NNQ

第二行一共N1N−1个数, 表示22号,3号,NN号节点的父节点。保证fat[i]<i

以下QQ行, 每行表示一组询问。

解题分析

这道题乍一看:woc不是裸的LCA吗?还出在考试里面…

然后看到了空间限制, 瞬间感觉不妙了起来。

8M只能开22106intint数组…倍增、树剖、tarjan显然都是不行的, 存个图就GG了…

那么我们咋搞?

因为每个点直接给的fat[i]fat[i], 所以我们可以不需要DFSDFS就能得到每个点的深度。 然后我们可以退而求其次, 模仿倍增LCALCA,每个点维护向上跳NN个点得到的位置。这样我们就可以O(QN)O(QN)解决问题。

但是这样还有问题: 我们需要开3个数组:fatfatdepdeptoto。这样还是会MLEMLE。 然而我们可以发现, 在父子关系中只需要一个点有depdep信息, 一个点有toto信息, 就可以推导出其他的。 所以我们可以把depdeptoto压在一起, 深度为奇数的时候保存toto信息, 深度为偶数的时候保存depdep信息。

代码如下:

#include <cstdio>
#include <cmath>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define MX 1000010
#define gc getchar()
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc);
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
}
struct int_3 {unsigned char h, m, l;} d[MX], fat[MX];
IN int get(const int_3 &x) {return (x.h << 14) + (x.m << 7) + x.l;}
IN int_3 to(R int x)
{
    int_3 ret;
    ret.l = x & 127;
    ret.m = (x >> 7) & 127;
    ret.h = x >> 14;
    return ret;
}
int dot, q, dp = 1024;
IN int F(R int x) {return get(fat[x]);}
IN int D(R int x) {return get(d[x]);}
IN int G(R int x) {return D(x) >= dot ? D(F(x)) + 1 : D(x);}
IN int LCA(R int x, R int y)
{
    if(G(x) < G(y)) std::swap(x, y);
    int ex = G(x) - G(y);
    W (ex > dp) if(D(x) < dot) x = F(x), --ex;
    else x = D(x) - dot, ex -= dp;
    W (ex--) x = F(x);
    W (F(x) != F(y))
    {
        if(D(x) == D(y)) x = F(x), y = F(y);
        else x = D(x) - dot, y = D(y) - dot;
    }
    if(x ^ y) x = F(x);
    return x;
}
int main(void)
{
    int a, b, now;
    in(dot), in(q); d[0] = to(dot);
    for (R int i = 2; i <= dot; ++i)
    in(a), fat[i] = to(a), d[i] = to(get(d[a]) + 1);
    for (R int i = dot; i; --i)
    {
        if(get(d[i]) & 1)
        {
            now = i;
            for (R int j = 1; j <= 2; ++j) now = get(fat[now]);
            d[i] = to(now + dot);//+dot来区分深度信息和跳父节点的信息。
        }
    }
    for (R int k = 9; k; --k)
    for (R int i = dot; i; --i)
    {
        if(get(d[i]) > dot)
        {
            now = i;
            for (R int j = 1; j <= 2; ++j) now = get(d[now]) - dot;
            d[i] = to(now + dot);
        }
    }
    W (q--) in(a), in(b), printf("%d\n", LCA(a, b));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值