正题
题目链接:https://www.luogu.com.cn/problem/P3292
题目大意
nnn个点的一棵树,每个点都点权。每次询问一条路径,选择若干个点的异或和最大。
解题思路
路径上的如何进行计算,我们知道我们可以用倍增来计算权值和。我们可以把每个线性基视为边权,然后加和就是线性基的合并。
合并线性基时我们将后面的所有did_idi都插入到前面那个线性基中即可。
时间复杂度O((n+q)log3n)O((n+q)log^3 n)O((n+q)log3n)
codecodecode
#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));}
}