LCA进阶-卡内存版

题目描述

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

2N1000000,1Q50000 2 ≤ N ≤ 1000000 , 1 ≤ Q ≤ 50000 ,时间限制 1000ms 1000 m s , 空间限制 8M 8 M

输入输出格式

输入格式

第一行两个个正整数 N N Q

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

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

解题分析

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

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

8M只能开 2 2 106 int i n t 数组…倍增、树剖、tarjan显然都是不行的, 存个图就GG了…

那么我们咋搞?

因为每个点直接给的 fat[i] f a t [ i ] , 所以我们可以不需要 DFS D F S 就能得到每个点的深度。 然后我们可以退而求其次, 模仿倍增 LCA L C A ,每个点维护向上跳 N N 个点得到的位置。这样我们就可以 O(QN) O ( Q N ) 解决问题。

但是这样还有问题: 我们需要开3个数组: fat f a t dep d e p to t o 。这样还是会 MLE M L E 。 然而我们可以发现, 在父子关系中只需要一个点有 dep d e p 信息, 一个点有 to t o 信息, 就可以推导出其他的。 所以我们可以把 dep d e p to t o 压在一起, 深度为奇数的时候保存 to t o 信息, 深度为偶数的时候保存 dep d e p 信息。

代码如下:

#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、付费专栏及课程。

余额充值