Cod sursa(job #2810839)

Utilizator lucamLuca Mazilescu lucam Data 30 noiembrie 2021 13:14:17
Problema Lowest Common Ancestor Scor 30
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 3.45 kb
#include <bits/stdc++.h>

#define BIT(x, bit) (bool((x) & (1 << (bit))))

template<typename It>
void print_iter(It l, It r) {
	for (It it = l; it != r; ++it) {
		std::clog << *it << " ";
	}
	std::clog << "\n";
}

template<typename T>
inline void set_bit(T &x, int bit, bool to) {
	if (to != bool(x & (1 << bit))) {
		x ^= 1 << bit;
	}
}

template<typename T, typename F>
void min_then(T &min, T x, F &&f) {
	if (x < min) {
		min = x;
		f();
	}
}

inline int log2_bit(int x) {
	return 31 - __builtin_clz(x);
}

std::ifstream in("lca.in");
std::ofstream out("lca.out");

const int N = 1e5;

int n;

std::vector<int> g[N];
int en;
int e[2 * N - 1];
int ep[N];

void prepare() {
	en = 2 * n - 1;
	std::function<void(int, int)> dfs = [&](int u, int k) {
		static int i;
		ep[u] = i;
		e[i++] = u;
		for (int v : g[u]) {
			dfs(v, k + 1);
			e[i++] = u;
		}
	};
	dfs(0, 0);
}

const int BS = 8, BN = N / BS + 1, BP = 1 << BS;
using Blk = unsigned char;

int bn, bs;
Blk blocks[BN];
struct BlockEntry {
	int mi;
	int l[BS], r[BS];
} bt[BP];

void process_block(Blk b, int cbs) {
	auto &ent = bt[b];
	if (ent.r[cbs - 1] != 0) {
		return;
	}

	int ps = 0;
	int min, mi;
	min = mi = 0;
	for (int i = 1; i < cbs; ++i) {
		ps += BIT(b, i - 1) ? 1 : -1;
		min_then(min, ps, [&] {
			mi = i;
		});
		ent.l[i] = mi;
	}
	ent.mi = mi;

	ps = min = 0;
	mi = cbs - 1;
	ent.r[mi] = mi;
	for (int i = cbs - 2; i >= 0; --i) {
		ps -= BIT(b, i) ? 1 : -1;
		min_then(min, ps, [&] {
			mi = i;
		});
		ent.r[i] = mi;
	}
}

void block() {
	bs = std::log2(en) / 2;
	bn = std::ceil(en / bs);
	for (int i = 0, bi = 0; i < en; i += bs, ++bi) {
		int cbs = std::min(bs, en - i);
		Blk b = (1 << bs) - 1;
		for (int j = 0; j < cbs - 1; ++j) {
			set_bit(b, j, e[i + j + 1] - e[i + j] > 0);
		}
		blocks[bi] = b;
		process_block(b, cbs);
	}
}

const int LOG_BN = 13;

int st[LOG_BN][BN];

inline int get_abs_pos_val(int bi, int i) {
	return e[bi * bs + i];
}

inline int get_block_min(int bi) {
	return get_abs_pos_val(bi, bt[blocks[bi]].mi);
}

void prepare_rmq() {
	for (int i = 0; i < bn; ++i) {
		st[0][i] = i;
	}
	auto min = [&](int bi1, int bi2) {
		int u1, u2;
		u1 = get_block_min(bi1);
		u2 = get_block_min(bi2);
		return u1 < u2 ? bi1 : bi2;
	};
	for (int exp = 1; (1 << exp) < bn; ++exp) {
		int p = 1 << exp;
		for (int i = 0; i + p < bn; ++i) {
			st[exp][i] = min(st[exp - 1][i], st[exp - 1][i + p / 2]);
		}
	}
}

int rmq(int l, int r) {
	int k = std::max(0, log2_bit(r - l + 1));
	int bi1, bi2;
	bi1 = st[k][l];
	bi2 = st[k][r - (1 << k) + 1];
	int u1, u2;
	u1 = get_block_min(bi1);
	u2 = get_block_min(bi2);
	return std::min(u1, u2);
}

int get_min_in_local_block(int i, bool left) {
	Blk b = blocks[i / bs];
	auto &v = left ? bt[b].l : bt[b].r;
	return v[i % bs];
}

int lca(int u, int v) {
	int up = ep[u];
	int vp = ep[v];
	if (up > vp) {
		std::swap(u, v);
		std::swap(up, vp);
	}
	int ubi = up / bs;
	int vbi = vp / bs;
	if (ubi == vbi) {
		return *std::min_element(e + up, e + vp + 1);
	}
	int min_right_u = get_abs_pos_val(ubi, get_min_in_local_block(up, false));
	int min_left_v = get_abs_pos_val(vbi, get_min_in_local_block(vp, true));
	if (vbi - ubi == 1) {
		return std::min(min_right_u, min_left_v);
	}
	return std::min(
		{min_right_u, min_left_v, rmq(ubi + 1, vbi - 1)});
}

int main() {
	int q;
	in >> n >> q;
	for (int i = 1; i < n; ++i) {
		int u;
		in >> u;
		--u;
		g[u].push_back(i);
	}
	prepare();
	block();
	prepare_rmq();
	for (int i = 0; i < q; ++i) {
		int u, v;
		in >> u >> v;
		--u, --v;
		out << lca(u, v) + 1 << "\n";
	}
}