Description
给定一棵树,每个点有一个权值。有若干询问,每次给定一条简单路径,问这条路径上的所有点中,选择一部分异或起来,可以得到的最大值是多少。
Solution
首先显然可以暴力树剖,用线段树维护线性基,只是这样复杂度略高 (O(q log^4 n))
用树上倍增维护,可以将复杂度降到 (O(q log^3 n))
考虑到对两个区间进行合并时,重复部分并不会影响,所以可以借鉴 ST 表的思路,将复杂度降到 (O(n log^3 n + q log^2 n))
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 20005;
const int lgN = 15;
struct linear_base {
int a[64];
linear_base() {
memset(a,0,sizeof a);
}
void insert(int k) {
for(int j=63; j>=0; --j)
if((k>>j)&1ll)
if(a[j]==0) {a[j]=k;break;}
else k^=a[j];
}
void join(linear_base x) {
for(int i=63;i>=0;--i) {
if(x.a[i]>0) insert(x.a[i]);
}
}
int solve() {
int ans = 0;
for(int i=63; i>=0; --i)
if((ans^a[i]) > ans) ans^=a[i];
return ans;
}
} lb[N][lgN+1];
vector <int> g[N];
int val[N],vis[N],t1,t2,t3,n,q,fa[N][lgN+1],dep[N];
void dfs(int p) {
vis[p]=1;
for(int q:g[p]) {
if(vis[q]==0) {
fa[q][0]=p;
dep[q]=dep[p]+1;
dfs(q);
}
}
}
void presolve() {
for(int i=1;i<=n;i++) lb[i][0].insert(val[i]);
for(int j=1;j<=lgN;j++) {
for(int i=1;i<=n;i++) {
fa[i][j]=fa[fa[i][j-1]][j-1];
lb[i][j].join(lb[i][j-1]);
lb[i][j].join(lb[fa[i][j-1]][j-1]);
}
}
}
int lca(int p,int q) {
if(dep[p]<dep[q]) swap(p,q);
for(int i=lgN;i>=0;--i) if(dep[fa[p][i]]>=dep[q]) p=fa[p][i];
for(int i=lgN;i>=0;--i) if(fa[p][i]-fa[q][i]) p=fa[p][i],q=fa[q][i];
if(p-q) return fa[p][0];
return p;
}
linear_base linequery(int p,int l) {
int len=dep[p]-dep[l]+1;
int lglen=log2(len);
linear_base ans;
ans.join(lb[p][lglen]);
int delta=len-(1ll<<lglen);
for(int i=lgN;i>=0;--i) if(delta&(1<<i)) p=fa[p][i];
ans.join(lb[p][lglen]);
return ans;
}
int query(int p,int q) {
int l=lca(p,q);
linear_base tmp;
tmp.join(linequery(p,l));
tmp.join(linequery(q,l));
return tmp.solve();
}
signed main() {
ios::sync_with_stdio(false);
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>val[i];
for(int i=1;i<n;i++) {
cin>>t1>>t2;
g[t1].push_back(t2);
g[t2].push_back(t1);
}
dep[1]=1;
dfs(1);
presolve();
for(int i=1;i<=q;i++) {
cin>>t1>>t2;
cout<<query(t1,t2)<<endl;
}
}