分析:
学长说这叫:树上线性基
有多重做法
(1)树链剖分+线性基合并(2)LCA+倍增+线性基合并(3)点分治+线性基合并
后两个是 logn l o g n 级的,第一种是 log2n l o g 2 n 级的
就代码复杂度来说,当然是选择第二种方法啦
每个结点维护向上跳
2i
2
i
步经过的数字的线性基
线性基的合并直接暴力即可
最后的统计答案就是普通的线性基求异或和最大
tip
我在倍增的时候没有在线性基中插入该点的权值
所以在询问的时候,需要单独加入两端点的权值:A.insert(val[x]); A.insert(val[y]);
LCA不要写错了,不然WA的太冤
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=20005;
struct po{
ll b[65];
void clear() {
memset(b,0,sizeof(b));
}
void insert(ll x) {
for (int i=63;i>=0;i--)
if (x>>i&1) {
if (b[i]) x^=b[i];
else {
b[i]=x;
break;
}
}
}
void Insert(po B) {
for (int i=63;i>=0;i--)
if (B.b[i])
insert(B.b[i]);
}
};
po a[N][20];
struct node{
int y,nxt;
};
node way[N<<1];
int st[N],tot=0,n,m,pre[N][20],deep[N],lg;
ll val[N];
void add(int u,int w) {
tot++;way[tot].y=w;way[tot].nxt=st[u];st[u]=tot;
tot++;way[tot].y=u;way[tot].nxt=st[w];st[w]=tot;
}
void dfs(int now,int fa,int dep) {
pre[now][0]=fa;
deep[now]=dep;
a[now][0].insert(val[fa]);
for (int i=st[now];i;i=way[i].nxt)
if (way[i].y!=fa)
dfs(way[i].y,now,dep+1);
}
void prepare() {
dfs(1,0,1);
lg=log(n)/log(2);
for (int i=1;i<=lg;i++)
for (int j=1;j<=n;j++) {
pre[j][i]=pre[pre[j][i-1]][i-1];
a[j][i]=a[j][i-1];
a[j][i].Insert(a[pre[j][i-1]][i-1]);
}
}
void solve(int x,int y) {
po A; A.clear();
A.insert(val[x]); A.insert(val[y]);
if (deep[x]<deep[y]) swap(x,y);
int d=deep[x]-deep[y];
if (d)
for (int i=0;i<=lg&&d;i++,d>>=1)
if (d&1) {
A.Insert(a[x][i]);
x=pre[x][i];
}
if (x!=y) {
for (int i=lg;i>=0;i--)
if (pre[x][i]!=pre[y][i]) {
A.Insert(a[x][i]);
A.Insert(a[y][i]);
x=pre[x][i];
y=pre[y][i];
}
A.insert(val[pre[x][0]]);
}
ll ans=0;
for (int i=63;i>=0;i--)
if (A.b[i]&&((ans^A.b[i])>ans))
ans^=A.b[i];
printf("%lld\n",ans);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%lld",&val[i]);
for (int i=1;i<n;i++) {
int u,w;
scanf("%d%d",&u,&w);
add(u,w);
}
prepare();
for (int i=1;i<=m;i++) {
int x,y;
scanf("%d%d",&x,&y);
solve(x,y);
}
return 0;
}