P3292
题目描述
题解
线性基合并,倍增
我们考虑一次询问
(
x
,
y
)
(x,y)
(x,y),
l
c
a
lca
lca表示他们的最近公共祖先,于是每次求答案时,我们维护x到y的路径上的数构成的线性基,于是我们考虑倍增处理,每次查询lca时,也维护出路径上的线性基,这里需要用到线性基合并,直接暴力合并即可,线性基合并的复杂度为
O
(
l
o
g
2
n
)
O(log^2n)
O(log2n),于是最后的总复杂度为
O
(
q
l
o
g
3
n
)
O(qlog^3n)
O(qlog3n)
代码
#include<bits/stdc++.h>
#define int long long
#define M 100001
using namespace std;
int read(){
int f=1,re=0;char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-'){f=-1,ch=getchar();}
for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
return re*f;
}
int nxt[M>>1],first[M],to[M>>1],dep[M],g[M][20][64],f[M][21],tot,n,q,val[M],ans[64];
void add(int x,int y){nxt[++tot]=first[x],first[x]=tot,to[tot]=y;}
void insert(int *p,int x){
for(int i=62;i>=0;i--)
if(x&(1ll<<i)){
if(!p[i]){p[i]=x;return;}
x^=p[i];
}
}
void merge(int *a,int *b){
for(int i=62;i>=0;i--)
if(a[i]) insert(b,a[i]);
}
int query(){
int sum=0;
for(int i=62;i>=0;i--)
if((sum^ans[i])>sum) sum^=ans[i];
return sum;
}
void dfs(int u,int fa){
dep[u]=dep[fa]+1,f[u][0]=fa,insert(g[u][0],val[u]);
for(int i=1;(1<<i)<=dep[u];i++){
f[u][i]=f[f[u][i-1]][i-1];
merge(g[u][i-1],g[u][i]);
merge(g[f[u][i-1]][i-1],g[u][i]);
}
for(int i=first[u];i;i=nxt[i]){
int v=to[i];
if(v==fa) continue;
dfs(v,u);
}
}
int getans(int x,int y){
memset(ans,0,sizeof(ans));
if(dep[x]<dep[y]) swap(x,y);
for(int i=19;i>=0;i--){
if(dep[f[x][i]]>=dep[y]){
merge(g[x][i],ans);
x=f[x][i];
}
}if(x==y){insert(ans,val[x]);return query();}
for(int i=19;i>=0;i--)
if(f[x][i]!=f[y][i]){
merge(g[x][i],ans),merge(g[y][i],ans);
x=f[x][i],y=f[y][i];
}
insert(ans,val[x]),insert(ans,val[y]),insert(ans,val[f[x][0]]);
return query();
}
signed main(){
n=read(),q=read();
for(int i=1;i<=n;i++) val[i]=read();
for(int i=1;i<n;i++){
int x=read(),y=read();
add(x,y),add(y,x);
}dfs(1,0);
for(int i=1;i<=q;i++){
int x=read(),y=read();
printf("%lld\n",getans(x,y));
}return 0;
}