Description
A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一。每座城市都有一个幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的象征。一些旅行者希望游览 A 国。旅行者计划乘飞机降落在 x 号城市,沿着 x 号城市到 y 号城市之间那条唯一的路径游览,最终从 y 城市起飞离开 A 国。在经过每一座城市时,游览者就会有机会与这座城市的幸运数字拍照,从而将这份幸运保存到自己身上。然而,幸运是不能简单叠加的,这一点游览者也十分清楚。他们迷信着幸运数字是以异或的方式保留在自己身上的。例如,游览者拍了 3 张照片,幸运值分别是 5,7,11,那么最终保留在自己身上的幸运值就是 9(5 xor 7 xor 11)。有些聪明的游览者发现,只要选择性地进行拍照,便能获得更大的幸运值。例如在上述三个幸运值中,只选择 5 和 11 ,可以保留的幸运值为 14 。现在,一些游览者找到了聪明的你,希望你帮他们计算出在他们的行程安排中可以保留的最大幸运值是多少。
Solution
看到求异或最大值,是可以用线性基的,想到线性基是可以合并的,就可以用树链剖分加线段树维护。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 20202;
typedef long long ll;
inline char get(void) {
static char buf[100000], *S = buf, *T = buf;
if (S == T) {
T = (S = buf) + fread(buf, 1, 100000, stdin);
if (S == T) return EOF;
}
return *S++;
}
template<typename T>
inline void read(T &x) {
static char c; x = 0;
for (c = get(); c < '0' || c > '9'; c = get());
for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
}
struct LB {
ll a[65];
inline void Clear(void) {
memset(a, 0, sizeof a);
}
inline LB(void) {
Clear();
}
inline ll Insert(ll x) {
for (int i = 60; i >= 0; i--)
if ((1ll << i) & x) {
if (!a[i]) {
a[i] = x; break;
}
x ^= a[i];
}
return x;
}
inline ll &operator [](int x) {
return a[x];
}
inline ll Max(void) {
ll mx = 0;
for (int i = 60; i >= 0; i--)
if ((mx ^ a[i]) > mx) mx ^= a[i];
return mx;
}
};
struct edge {
int to, next;
edge(int t = 0, int n = 0):to(t), next(n) {}
};
edge G[N << 1];
int head[N];
ll val[N];
int fa[N], dep[N], size[N], top[N], pos[N], son[N];
int Gcnt, Pcnt, n, q, x, y, f1, f2;
LB T[N << 3];
LB ans;
inline void AddEdge(int from, int to) {
G[++Gcnt] = edge(to, head[from]); head[from] = Gcnt;
G[++Gcnt] = edge(from, head[to]); head[to] = Gcnt;
}
inline LB Merge(LB &a, LB &b) {
LB c = a;
for (int i = 60; i >= 0; i--)
if (b[i]) c.Insert(b[i]);
return c;
}
void dfs1(int u) {
size[u] = 1;
for (int i = head[u]; i; i = G[i].next) {
if (fa[u] == G[i].to) continue;
dep[G[i].to] = dep[u] + 1;
fa[G[i].to] = u; dfs1(G[i].to);
size[u] += size[G[i].to];
if (size[son[u]] < size[G[i].to]) son[u] = G[i].to;
}
}
void dfs2(int u, int t) {
top[u] = t; pos[u] = ++Pcnt;
if (son[u]) dfs2(son[u], t);
for (int i = head[u]; i; i = G[i].next)
if (son[u] != G[i].to && fa[u] != G[i].to)
dfs2(G[i].to, G[i].to);
}
void Insert(int o, int l, int r, int pos, ll v) {
if (l == r) return (void)(T[o].Insert(v));
int mid = (l + r) >> 1;
if (pos <= mid) Insert(o << 1, l, mid, pos, v);
else Insert(o << 1 | 1, mid + 1, r, pos, v);
T[o] = Merge(T[o << 1], T[o << 1 | 1]);
}
void Query(int o, int l, int r, int L, int R) {
if (l >= L && r <= R) return (void)(ans = Merge(T[o], ans));
int mid = (l + r) >> 1; LB res;
if (L <= mid) Query(o << 1, l, mid, L, R);
if (R > mid) Query(o << 1 | 1, mid + 1, r, L, R);
}
int main(void) {
read(n); read(q);
for (int i = 1; i <= n; i++) read(val[i]);
for (int i = 1; i < n; i++) {
read(x); read(y);
AddEdge(x, y);
}
dfs1(1); dfs2(1, 1);
for (int i = 1; i <= n; i++)
Insert(1, 1, n, pos[i], val[i]);
while (q--) {
read(x); read(y); ans.Clear();
f1 = top[x]; f2 = top[y];
while (f1 != f2) {
if (dep[f1] < dep[f2]) {
swap(x, y); swap(f1, f2);
}
Query(1, 1, n, pos[f1], pos[x]);
x = fa[f1]; f1 = top[x];
}
if (dep[x] > dep[y]) swap(x, y);
Query(1, 1, n, pos[x], pos[y]);
printf("%lld\n", ans.Max());
}
return 0;
}