幸运数字
题目链接:luogu P3292
题目大意
给你一个树,点有点权。
多次询问,每次问你树上的一条路径,它上面所有点任选记得,使得它们点权的异或和最大。
思路
看到树上路径——倍增!
看到异或——线性基!
那我们考虑能不能直接暴力,发现线性基可以合并,而且
n
l
o
g
3
n
nlog^3n
nlog3n 刚好勉勉强强能过。
然后就直接上了。
线性基的合并其实就是将一个线性基中的非
0
0
0 数全部加入另一个线性基中就可以了。
不难看出复杂度是
l
o
g
2
n
log^2n
log2n,然后倍增
n
l
o
g
n
nlogn
nlogn,倍增的时候要合并,所以是
n
l
o
g
3
n
nlog^3n
nlog3n。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
struct node {
int to, nxt;
}e[40001];
int n, q, fa[20001][16], deg[20001];
int le[20001], KK, x, y;
ll val[20001], d[20001][16][61];
void add(int x, int y) {
e[++KK] = (node){y, le[x]}; le[x] = KK;
e[++KK] = (node){x, le[y]}; le[y] = KK;
}
//线性基
void xxj_add(int x, int y, ll now) {
for (int i = 60; i >= 0; i--)
if ((now >> i) & 1) {
if (!d[x][y][i]) {
d[x][y][i] = now;
break;
}
now ^= d[x][y][i];
}
}
//线性基合并
void add_xxj(int x, int y, int xx, int yy) {
for (int i = 60; i >= 0; i--)
if (d[xx][yy][i])
xxj_add(x, y, d[xx][yy][i]);
}
void dfs(int now, int father) {
deg[now] = deg[father] + 1;
fa[now][0] = father;
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].to != father) {
dfs(e[i].to, now);
}
}
int LCA(int x, int y) {
if (deg[y] > deg[x]) swap(x, y);
for (int i = 15; i >= 0; i--)
if (deg[fa[x][i]] >= deg[y]) {
add_xxj(0, 0, x, i);
x = fa[x][i];
}
if (x == y) {
add_xxj(0, 0, x, 0);//由于是点权,那最后一个点不会被算到,要并进去
return x;
}
for (int i = 15; i >= 0; i--)
if (fa[x][i] != fa[y][i]) {
add_xxj(0, 0, x, i);
add_xxj(0, 0, y, i);
x = fa[x][i];
y = fa[y][i];
}
add_xxj(0, 0, x, 0);
add_xxj(0, 0, y, 0);
add_xxj(0, 0, fa[x][0], 0);//跟上面同理
return fa[x][0];
}
ll get_max(int x, int y) {
ll re = 0;
for (int i = 60; i >= 0; i--)
if ((re ^ d[x][y][i]) > re)
re ^= d[x][y][i];
return re;
}
int main() {
scanf("%d %d", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%lld", &val[i]);
xxj_add(i, 0, val[i]);
}
for (int i = 1; i < n; i++) {
scanf("%d %d", &x, &y);
add(x, y);
}
dfs(1, 0);
for (int i = 1; i <= 15; i++)
for (int j = 1; j <= n; j++) {
fa[j][i] = fa[fa[j][i - 1]][i - 1];
add_xxj(j, i, j, i - 1);//暴力合并
add_xxj(j, i, fa[j][i - 1], i - 1);
}
while (q--) {
scanf("%d %d", &x, &y);
memset(d[0][0], 0, sizeof(d[0][0]));
LCA(x, y);
printf("%lld\n", get_max(0, 0));
}
return 0;
}