P3292 [SCOI2016] 幸运数字 做题记录
题目链接:P3292 [SCOI2016] 幸运数字 做题记录
这道题是一道线性基合并的题目。
首先发现 LCA 可以倍增实现,而这道题又是集合最大异或和,一眼线性基。
参考并查集倍增,我们可以考虑线性倍增与合并。
我们假设维护一个倍增线性基 \(BASE[x][i]\) 表示从 \(x\) 开始,向上跳 \(2^i\) 个祖先,这一条路径上的线性基。
那么很好转移:\(BASE[x][i]=merge(BASE[x][i-1],BASE[f[x][i-1]][i-1])\)。
考虑 merge 函数的实现就是将线性基 \(A\) 的所有元素插入到 \(B\) 内即可。
注意 corner case。如果 \(x=y\),那么要直接输出 \(a[x]\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e4+5;
vector<int> G[N];
int n,m,dep[N],f[N][16],a[N];
struct LB
{
int p[65];
void clear(){memset(p,0,sizeof(p));}
void ins(int x)
{
for(int i=61;~i;i--)
{
if(~(x>>i)&1) continue;
if(!p[i])
{
p[i]=x;
break;
}
x^=p[i];
}
}
friend LB operator +(const LB &A,const LB &B)
{
LB T=A;
for(int i=61;~i;i--)
if(B.p[i]) T.ins(B.p[i]);
return T;
}
}B[N][16];
void dfs(int x,int fa)
{
f[x][0]=fa,dep[x]=dep[fa]+1;B[x][0].clear();
B[x][0].ins(a[x]),B[x][0].ins(a[fa]);
for(int i=1;i<=15;i++)
{
f[x][i]=f[f[x][i-1]][i-1];
B[x][i].clear();
B[x][i]=B[x][i-1]+B[f[x][i-1]][i-1];
}
for(int v:G[x])
{
if(v==fa) continue;
dfs(v,x);
}
}
LB LCA(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
LB T;T.clear();
for(int i=15;~i;i--)
if(dep[f[x][i]]>=dep[y])
T=T+B[x][i],x=f[x][i];
if(x==y) return T;
for(int i=15;~i;i--)
{
if(f[x][i]==f[y][i]) continue;
T=T+B[x][i]+B[y][i];
x=f[x][i],y=f[y][i];
}
return T+B[x][0]+B[y][0];
}
signed main()
{
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<n;i++)
{
int x,y;cin>>x>>y;
G[x].push_back(y);
G[y].push_back(x);
}
dfs(1,0);
while(m--)
{
int x,y;cin>>x>>y;
if(x==y)
{
cout<<a[x]<<"\n";
continue;
}
LB T;T.clear();
T=LCA(x,y);
int res=0;
for(int i=61;~i;i--)
res=max(res,res^T.p[i]);
cout<<res<<"\n";
}
return 0;
}

浙公网安备 33010602011771号