題目連結:https://www.luogu.com.cn/problem/P3292
n n n個點的一棵樹,每個點都點權。每次詢問一條路徑,選擇若干個點的互斥或和最大。
路徑上的如何進行計算,我們知道我們可以用倍增來計算權值和。我們可以把每個線性基視為邊權,然後加和就是線性基的合併。
合併線性基時我們將後面的所有 d i d_i di都插入到前面那個線性基中即可。
時間複雜度 O ( ( n + q ) l o g 3 n ) O((n+q)log^3 n) O((n+q)log3n)
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=21000,T=15;
struct node{
ll to,next;
}a[N*2];
struct xxj{
ll d[80];
ll solve(){
ll ans=0;
for(ll i=60;i>=0;i--)
if((ans^d[i])>ans)ans^=d[i];
return ans;
}
void add(ll x){
if(!x)return;
for(ll i=60;i>=0;i--)
if((x>>i)&1ll){
if(d[i])x^=d[i];
else{
d[i]=x;
return;
}
}
}
}w[N][T];
ll n,q,tot,ls[N],dep[N],g[N],f[N][T];
xxj operator+(const xxj &a,const xxj &b){
xxj ans=a;
for(ll i=0;i<=60;i++)
if(b.d[i])ans.add(b.d[i]);
return ans;
}
void addl(ll x,ll y){
a[++tot].to=y;
a[tot].next=ls[x];
ls[x]=tot;
}
void dfs(ll x){
dep[x]=dep[f[x][0]]+1;
for(ll i=ls[x];i;i=a[i].next){
ll y=a[i].to;
if(y==f[x][0])continue;
f[y][0]=x;dfs(y);
}
return;
}
void ycl(){
for(ll i=1;i<=n;i++)
w[i][0].add(g[i]);
for(ll j=1;j<T;j++){
for(ll i=1;i<=n;i++){
f[i][j]=f[f[i][j-1]][j-1];
w[i][j]=w[i][j-1]+w[f[i][j-1]][j-1];
}
}
return;
}
ll LCA(ll x,ll y){
xxj ans;memset(ans.d,0,sizeof(ans.d));
if(dep[x]>dep[y])swap(x,y);
for(ll i=T-1;i>=0;i--)
if(dep[f[y][i]]>=dep[x])
ans=ans+w[y][i],y=f[y][i];
if(x==y){
ans.add(g[x]);
return ans.solve();
}
for(ll i=T-1;i>=0;i--)
if(f[x][i]!=f[y][i])
ans=ans+w[x][i]+w[y][i],
x=f[x][i],y=f[y][i];
ans=ans+w[x][0]+w[y][0];
ans.add(g[f[x][0]]);
return ans.solve();
}
int main()
{
scanf("%lld%lld",&n,&q);
for(ll i=1;i<=n;i++)
scanf("%lld",&g[i]);
for(ll i=1;i<n;i++){
ll x,y;
scanf("%lld%lld",&x,&y);
addl(x,y);addl(y,x);
}
dfs(1);ycl();
while(q--){
ll x,y;
scanf("%lld%lld",&x,&y);
printf("%lld\n",LCA(x,y));
}
}