题目大意
给你一颗
N
个节点的树,每个节点都有一个权值
N≤2∗104
M≤2∗105
Ai≤260
解题思路
看到这种区间异或最大值的题,那么我们就要联想到线性基,是一个只用维护
Log(maxAi)
个值就可以表示出区间所有的值的算法,并且合并是
Log(maxAi)2
。
那么如果会线性基后,就可以先预处理出数组
F[i][j]
表示从i这个节点到以i向上
2j
个节点的这段区间的线性基,然后对于每个询问,有倍增的思想来合并预处理好的线性基就可以了。
那么现在的问题就是什么是线性基?
线性基
主要思想
线性基是一个主要解决有关异或方面的问题。它的主要思想就是用尽可能少的数表示出集合中的所有数。根据定义我们就可以得出线性基的几个性质。
1. 对于集合的每一个数都可以有线性基中的是异或而来。
2. 线性基中的每个数都不可以有线性基中的其他数异或而来。
具体操作
实际上对于每组线性基只需要在二进制上的每一位存一个数就可以了。这个很好理解,这种思想跟高斯消元很像,因为每一个第i位为1的数都可以与集合中其他第i为1的数异或,操作一次后,就只剩下当前数的这一位为1,所以说一个线性基只用存 Log(maxAi) 个数。
合并两个线性基
合并两个线性基时只需把其中一个线性基的数暴力加到另一个中就可以了。加入时的操作也跟高斯消元类似,我们首先需要判断的就是当前这个数能否被另一个线性基中的数表示。加入当前待加入的这个数的第i位为1,那么我们就要判断另一个线性基中的第i位有没有存下数字。如果有那么这一位就可以被消去,如果没有那么这个数就不能被另一个线性基表示,所以在第i位加入这个数。所以合并两个线性基的复杂度是 O(Log(maxAi)2) 的。
程序
//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 2e4 + 5;
LL F[MAXN][16][65], Ans[65];
int N, M, Deep[MAXN], Fa[MAXN][16];
int tot, Last[MAXN], Next[MAXN * 2], Go[MAXN * 2];
void Link(int u, int v) {
Next[++ tot] = Last[u], Last[u] = tot, Go[tot] = v;
}
void Merge(LL *F, LL Now) {
for (int i = 60; i + 1; i --) {
if ((Now >> i) & 1ll) {
if (!F[i]) {
F[i] = Now;
return;
} else Now ^= F[i];
}
}
}
void MergeAll(LL *F, LL *G) {
for (int i = 60; i + 1; i --)
if (G[i]) Merge(F, G[i]);
}
void Dfs(int Now, int Pre) {
Deep[Now] = Deep[Pre] + 1;
Fa[Now][0] = Pre;
for (int p = Last[Now]; p; p = Next[p])
if (Go[p] != Pre) Dfs(Go[p], Now);
}
void Prepare() {
for (int j = 1; j <= 15; j ++)
for (int i = 1; i <= N; i ++) {
Fa[i][j] = Fa[Fa[i][j - 1]][j - 1];
memcpy(F[i][j], F[i][j - 1], sizeof F[i][j - 1]);
MergeAll(F[i][j], F[Fa[i][j - 1]][j - 1]);
}
}
void Lca(int x, int y) {
if (Deep[x] < Deep[y]) swap(x, y);
for (int i = 15; i + 1; i --)
if (Deep[Fa[x][i]] >= Deep[y]) {
MergeAll(Ans, F[x][i]);
x = Fa[x][i];
}
if (x == y) {
MergeAll(Ans, F[x][0]);
return;
}
for (int i = 15; i + 1; i --)
if (Fa[x][i] != Fa[y][i]) {
MergeAll(Ans, F[x][i]), MergeAll(Ans, F[y][i]);
x = Fa[x][i], y = Fa[y][i];
}
MergeAll(Ans, F[x][0]), MergeAll(Ans, F[y][0]);
MergeAll(Ans, F[Fa[x][0]][0]);
}
void Solve() {
for (int i = 1; i <= M; i ++) {
memset(Ans, 0, sizeof Ans);
int u, v;
scanf("%d%d", &u, &v);
Lca(u, v);
LL Sum = 0;
for (int j = 60; j + 1; j --) Sum = max(Sum, Sum ^ Ans[j]);
printf("%lld\n", Sum);
}
}
int main() {
scanf("%d%d", &N, &M);
for (int i = 1; i <= N; i ++) {
LL Now;
scanf("%lld", &Now);
Merge(F[i][0], Now);
}
for (int i = 1; i < N; i ++) {
int u, v;
scanf("%d%d", &u, &v);
Link(u, v), Link(v, u);
}
Dfs(1, 0);
Prepare();
Solve();
}