[ SCOI 2016 ] 幸运数字
题目
思路
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#define int long long
using namespace std;
const int N = 20010;
int n, m, B[N][61], P[N][61];
int h[N], ptr[N << 1], val[N << 1], idx;
int base[N], f[N][21], dep[N], w[N];
void add(int a, int b) { val[idx] = b, ptr[idx] = h[a], h[a] = idx++; }
void insert(int u, int B[], int P[]) {
int x = w[u];
for (int i = 60; i >= 0; i--) {
if (!(x >> i & 1)) continue;
if (!B[i]) { B[i] = x, P[i] = u; break; } // 不存在线性基
if (dep[u] > dep[P[i]]) swap(P[i], u), swap(x, B[i]); // 比较一下深度
x ^= B[i];
}
}
void DFS(int u, int fa) {
f[u][0] = fa, dep[u] = dep[fa] + 1;
for (int i = 1; i <= 20; i++) // LCA 预处理父亲
f[u][i] = f[f[u][i - 1]][i - 1];
for (int i = 0; i <= 60; i++) // 复制父亲信息
P[u][i] = P[fa][i], B[u][i] = B[fa][i];
insert(u, B[u], P[u]); // 用自己更新一下
for (int i = h[u]; i != -1; i = ptr[i])
if (val[i] != fa) DFS(val[i], u);
}
int LCA(int a, int b) { // 倍增LCA
if (dep[a] < dep[b]) swap(a, b);
for (int i = 20; i >= 0; i--)
if (dep[f[a][i]] >= dep[b]) a = f[a][i];
if (a == b) return a;
for (int i = 20; i >= 0; i--)
if (f[a][i] != f[b][i])
a = f[a][i], b = f[b][i];
return f[a][0];
}
int query(int a, int b) {
int t = LCA(a, b);
for (int i = 60; i >= 0; i--) // 首先把 a 到 LCA 的线性基全复制过来
(dep[P[a][i]] >= dep[t]) ? base[i] = B[a][i] : base[i] = 0;
for (int i = 60; i >= 0; i--) {
if (dep[P[b][i]] < dep[t]) continue;
int x = B[b][i]; // 暴力插入 b 到 LCA 的线性基
for (int j = 60; j >= 0; j--)
if (!(x >> j & 1)) continue;
else if (!base[j]) { base[j] = x; break; }
else x ^= base[j];
}
int res = 0; // 最大值
for (int i = 60; i >= 0; i--)
res = max(res, base[i] ^ res);
return res;
}
signed main() {
cin >> n >> m;
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i++) cin >> w[i];
for (int i = 1, a, b; i < n; i++)
cin >> a >> b, add(a, b), add(b, a);
DFS(1, 0);
for (int a, b; m-- && cin >> a >> b; )
cout << query(a, b) << endl;
return 0;
}