【洛谷P3812】【模板】线性基

题目

题目链接:https://www.luogu.com.cn/problem/P3812
给定 \(n\) 个整数(数字可能重复),求在这些数中选取任意个,使得他们的异或和最大。

思路

一个值域为 \([0,A]\) 的序列的线性基长度为 \(\lceil\log A\rceil\)。并且通过线性基内元素互相异或,可以得到原序列中每一个数字。
具体的,我们设 \(d\)\(a\) 的线性基,那么 \(d_i\) 表示 \(a\) 中二进制最高位的 \(1\) 是第 \(i\) 位的一个数。假设我们要插入一个数 \(x\),那么我们就不断重复以下过程:

  1. 找到 \(x\) 的最高位的 \(1\),判断该位置 \(d\) 是否有数字。如果没有则赋值后直接 break。
  2. 否则设 \(x\) 最高位的 \(1\) 在位置 \(i\),那么让 \(x\gets x\ \mathrm{xor}\ d_i\)
  3. 如果 \(x\)\(0\) 则直接退出。

因为每次操作二后 \(x\) 最高位的 \(1\) 都会变成 \(0\),所以复杂度是 \(O(\log x)\) 的。
这题要求异或和最大,我们就从高位到低位枚举,如果此时的答案 \(res\)\(i\) 位为 \(0\),那么就让 \(res\gets res\ \mathrm{xor}\ d_i\)。这样显然每次操作不会更劣。
时间复杂度 \(O(n\log n)\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=55;
int n;
ll x,d[N];
bool flag;

void insert(ll x)
{
	for (int i=N-1;i>=0;i--)
		if (x&(1LL<<i))
		{
			if (!d[i]) { d[i]=x; return; }
			x^=d[i];
		}
	flag=1;
}

ll getmax()
{
	ll ans=0;
	for (int i=N-1;i>=0;i--)
		if (!(ans&(1LL<<i))) ans^=d[i];
	return ans;
}

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%lld",&x);
		insert(x);
	}
	printf("%lld",getmax());
	return 0;
}
posted @ 2021-01-03 15:07  stoorz  阅读(67)  评论(0编辑  收藏  举报