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;
}
posted @ 2025-04-29 17:39  I_AK_CTSC  阅读(32)  评论(0)    收藏  举报