[洛谷P3292] [SCOI2016]幸运数字
P3292 [SCOI2016]幸运数字
一颗带点权的树,每次查询指定两个点,求在两个点之间的这条路径(包括两端)上选任意个数能得到的最大异或值。
做法
考虑倍增维护LCA,同时维护向上\(2^k\)的点的线性基,然后查询求LCA时将经过的所有线性基暴力合并即可。
#include <cmath>
#include <cstdio>
#include <cstring>
template <class T>
inline void read(T &num)
{
bool flag = 0;
num = 0;
char c = getchar();
while ((c < '0' || c > '9') && c != '-')
c = getchar();
if (c == '-')
{
flag = 1;
c = getchar();
}
num = c - '0';
c = getchar();
while (c >= '0' && c <= '9')
num = (num << 3) + (num << 1) + c - '0', c = getchar();
if (flag)
num *= -1;
}
template <class T>
inline void output(T num)
{
if (num < 0)
{
putchar('-');
num = -num;
}
if (num >= 10)
output(num / 10);
putchar(num % 10 + '0');
}
template <class T>
inline void outln(T num)
{
output(num);
putchar('\n');
}
template <class T>
inline void outps(T num)
{
output(num);
putchar(' ');
}
template <class T>
inline T max(T a, T b)
{
return a < b ? b : a;
}
typedef long long ll;
struct basis // 线性基
{
typedef ll type;
static const int W = 62;
type s[W];
basis()
{
memset(s, 0, sizeof(s));
}
void ins(type x) // 向线性基内插入一个数
{
for (int i = W; i >= 1; i--)
{
if (x >> (i - 1))
{
if (s[i] == 0)
{
s[i] = x;
return;
}
x ^= s[i];
}
}
}
type maxxor() // 求线性基内最大xor值
{
type ans = 0;
for (int i = W; i >= 1; i--)
{
ans = max(ans, ans ^ s[i]);
}
return ans;
}
};
const int N = 20005;
const int lN = 20;
int n, q, ln;
namespace graph // 存图
{
int fir[N], to[N * 2], nxt[N * 2], ecnt;
void add_edge(int u, int v)
{
to[++ecnt] = v;
nxt[ecnt] = fir[u];
fir[u] = ecnt;
}
} // namespace graph
namespace tree
{
ll g[N]; // g[i] : i号点的数字
int dep[N]; // dep[i] : i号点的深度
int fa[N][lN]; // fa[i][j] : i号点向上跳2^j到达的节点
basis b[N][lN]; // b[i][j] : i号点以上的2^j点的线性基(显然不包括fa[i][j])
void dfs(int node, int f) // 深搜初始化
{
using namespace graph;
dep[node] = dep[f] + 1;
fa[node][0] = f;
b[node][0].ins(g[node]);
for (int i = 1; i <= ln; i++) // 倍增求2^k次祖先以及路径上数字的线性基
{
fa[node][i] = fa[fa[node][i - 1]][i - 1];
if (!fa[node][i])
break;
for (int j = 1; j <= basis::W; j++)
{
if (b[node][i - 1].s[j])
b[node][i].ins(b[node][i - 1].s[j]);
if (b[fa[node][i - 1]][i - 1].s[j])
b[node][i].ins(b[fa[node][i - 1]][i - 1].s[j]);
}
}
for (int e = fir[node]; e; e = nxt[e])
{
if (to[e] == f)
continue;
dfs(to[e], node);
}
}
} // namespace tree
ll solve(int x, int y)
{
using namespace tree;
basis rtn;
if (dep[x] < dep[y])
x ^= y ^= x ^= y;
for (int k = ln; k >= 0; k--)
if (dep[fa[x][k]] >= dep[y])
{
for (int j = 1; j <= basis::W; j++)
if (b[x][k].s[j])
rtn.ins(b[x][k].s[j]);
x = fa[x][k];
}
if (x == y)
{
rtn.ins(g[x]);
return rtn.maxxor();
}
for (int i = ln; i >= 0; i--)
{
if (dep[fa[x][i]] == 0)
continue;
if (fa[x][i] == fa[y][i])
continue;
for (int j = 1; j <= basis::W; j++)
{
if (b[x][i].s[j])
rtn.ins(b[x][i].s[j]);
if (b[y][i].s[j])
rtn.ins(b[y][i].s[j]);
}
x = fa[x][i];
y = fa[y][i];
}
rtn.ins(g[x]);
rtn.ins(g[y]);
rtn.ins(g[fa[x][0]]);
return rtn.maxxor();
}
int main()
{
read(n);
read(q);
ln = ceil(log(n) / log(2)) + 2;
for (int i = 1; i <= n; i++)
read(tree::g[i]);
for (int i = 1; i < n; i++)
{
int x, y;
read(x);
read(y);
graph::add_edge(x, y);
graph::add_edge(y, x);
}
tree::dfs(1, 0);
while (q--)
{
int x, y;
read(x);
read(y);
outln(solve(x, y));
}
}