BZOJ4568: [Scoi2016]幸运数字
线性基·倍增·LCA
题解:
抑或和最大的问题显然要用到线性基。
本题就直接倍增维护线性基,合并出答案即可。
线性基的合并就是一个插入到另一个中。
有一点小细节就是lb[i][j]中维护的是从i到i的2j级祖先的线性基,左开右闭,也就是不包括i本身的。因此查lca(a,b)的时候先把a和b的插入进去。
Code:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long LL;
const int N = 20005;
const int LOG = 61;
int n,q; LL g[N];
int dep[N],pa[N][21];
struct Edge{
int to,next;
} e[N*2];
int head[N], ec;
void add(int a,int b){
ec++; e[ec].to=b; e[ec].next=head[a]; head[a]=ec;
}
void input(){
cin>>n>>q;
for(int i=1;i<=n;i++) scanf("%lld",g+i);
int a,b;
for(int i=1;i<n;i++){
scanf("%d%d",&a,&b);
add(a,b); add(b,a);
}
}
struct LB{
LL d[LOG+1];
LB(){ memset(d,0,sizeof(d)); }
void insert(LL x){
for(int j=LOG;j>=0;j--){
if((x>>j)&1){
if(!d[j]){ d[j]=x; break; }
x^=d[j];
}
}
}
LL getMax(){
LL x=0;
for(int i=LOG;i>=0;i--){
if((x^d[i])>x) x^=d[i];
}
return x;
}
} lb[N][21];
void un(LB &a,const LB &b){
for(int i=0;i<=LOG;i++){
if(b.d[i]) a.insert(b.d[i]);
}
}
void dfs(int u,int f){
dep[u]=dep[f]+1; pa[u][0]=f; lb[u][0].insert(g[f]);
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v==f) continue;
dfs(v,u);
}
}
void init(){
for(int j=1;j<=20;j++){
for(int i=1;i<=n;i++){
pa[i][j]=pa[pa[i][j-1]][j-1];
un(lb[i][j],lb[i][j-1]);
un(lb[i][j],lb[pa[i][j-1]][j-1]);
}
}
}
LB LCA(int a,int b){
LB res; res.insert(g[a]); res.insert(g[b]);
if(dep[a]<dep[b]) swap(a,b);
int cha=dep[a]-dep[b];
for(int j=20;j>=0;j--){
if((cha>>j)&1){
un(res,lb[a][j]);
a=pa[a][j];
}
}
if(a!=b){
for(int j=20;j>=0;j--){
if(pa[a][j]!=pa[b][j]){
un(res,lb[a][j]);
un(res,lb[b][j]);
a=pa[a][j]; b=pa[b][j];
}
}
un(res,lb[a][0]);
a=pa[a][0];
}
return res;
}
void solve(){
int a,b;
while(q--){
scanf("%d%d",&a,&b);
LB res=LCA(a,b);
printf("%lld\n",res.getMax());
}
}
int main(){
freopen("a.in","r",stdin);
input();
dfs(1,0);
init();
solve();
}