题:https://loj.ac/problem/2013
题:https://www.luogu.com.cn/problem/P3292
题意:给定n个节点的树,每个节点都有权值,q组查询[u, v]查询u到v的最短路径上的点权能组成的最大异或和;
分析:朴素地想,肯定是每次u到v上的点全部塞到线性基中,然后找出最大值即可,但是朴素地TLE;
因此我们可以利用树上找路径倍增求LCA,因为线性基的大小只有log级别,所以我们在处理倍增的时候顺便把向上祖先路径的线性基预处理一下;
在solve时也顺理成章地合并“一段一段”的线性基即可。
#include<bits/stdc++.h> using namespace std; #define pb push_back #define MP make_pair #define lson root<<1,l,midd #define rson root<<1|1,midd+1,r typedef long long ll; const int M=60; const int N=2e4+4; struct LB{ ll a[M+2]; void init(){ memset(a,0,sizeof(a)); } void Insert(ll x){ for(int i=M;i>=0;i--){ if(!(x&(1ll<<i))) continue; if(a[i]) x^=a[i]; else{ for(int j=0;j<i;j++) if(x&(1ll<<j)) x^=a[j]; for(int j=i+1;j<=M;j++) if(a[j]&(1ll<<i)) a[j]^=x; a[i]=x; return ; } } } ll getmax(){ ll res=0; for(int i=M;i>=0;i--) res=max(res,res^a[i]); return res; } LB operator + (const LB &other)const{ LB res = *this; for(int i=0;i<=M;i++) if(other.a[i]) res.Insert(other.a[i]); return res; } }lb[N][15]; int f[N][15],deep[N],lg[N]; vector<int>g[N]; ll val[N]; void dfs(int u,int fa){ deep[u]=deep[fa]+1; lb[u][0].init(); lb[u][0].Insert(val[u]); f[u][0]=fa; for(int i=1;i<=lg[deep[u]];i++){ f[u][i]=f[f[u][i-1]][i-1]; lb[u][i]=lb[u][i-1]+lb[f[u][i-1]][i-1]; } for(auto v:g[u]) if(v!=fa) dfs(v,u); } ll solve(int u,int v){ LB tmp; tmp.init(); if(deep[u]<deep[v]) swap(u,v); while(deep[u]>deep[v]){ tmp = tmp + lb[u][lg[deep[u]-deep[v]]-1]; u=f[u][lg[deep[u]-deep[v]]-1]; } if(u==v){ tmp.Insert(val[u]); return tmp.getmax(); } for(int i=lg[deep[u]]-1;i>=0;i--) if(f[u][i]!=f[v][i]) tmp = tmp + lb[u][i] + lb[v][i], u=f[u][i], v=f[v][i]; tmp.Insert(val[f[u][0]]); tmp.Insert(val[u]); tmp.Insert(val[v]); return tmp.getmax(); } int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%lld",&val[i]); for(int u,v,i=1;i<n;i++){ scanf("%d%d",&u,&v); g[u].pb(v),g[v].pb(u); } for(int i=1;i<=n;i++) lg[i]=lg[i-1]+(1<<lg[i-1]==i); dfs(1,0); for(int u,v,i=1;i<=m;i++){ scanf("%d%d",&u,&v); printf("%lld ",solve(u,v)); } return 0; }