题目描述
给定一棵以 1 1 号节点为根,由个节点构成的有根树, 共 Q Q 组询问, 每次询问两点间的。
2≤N≤1000000,1≤Q≤50000 2 ≤ N ≤ 1000000 , 1 ≤ Q ≤ 50000 ,时间限制 1000ms 1000 m s , 空间限制 8M 8 M 。
输入输出格式
输入格式
第一行两个个正整数 N N ,。
第二行一共 N−1 N − 1 个数, 表示 2 2 号,号, ⋯ ⋯ , N N 号节点的父节点。保证
以下 Q Q 行, 每行表示一组询问。
解题分析
这道题乍一看:woc不是裸的LCA吗?还出在考试里面…
然后看到了空间限制, 瞬间感觉不妙了起来。
只能开 2 2 个的 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));
}