题目
题目链接:https://www.luogu.com.cn/problem/P3812
给定 (n) 个整数(数字可能重复),求在这些数中选取任意个,使得他们的异或和最大。
思路
一个值域为 ([0,A]) 的序列的线性基长度为 (lceillog A
ceil)。并且通过线性基内元素互相异或,可以得到原序列中每一个数字。
具体的,我们设 (d) 是 (a) 的线性基,那么 (d_i) 表示 (a) 中二进制最高位的 (1) 是第 (i) 位的一个数。假设我们要插入一个数 (x),那么我们就不断重复以下过程:
- 找到 (x) 的最高位的 (1),判断该位置 (d) 是否有数字。如果没有则赋值后直接 break。
- 否则设 (x) 最高位的 (1) 在位置 (i),那么让 (xgets x mathrm{xor} d_i)。
- 如果 (x) 为 (0) 则直接退出。
因为每次操作二后 (x) 最高位的 (1) 都会变成 (0),所以复杂度是 (O(log x)) 的。
这题要求异或和最大,我们就从高位到低位枚举,如果此时的答案 (res) 第 (i) 位为 (0),那么就让 (resgets res mathrm{xor} d_i)。这样显然每次操作不会更劣。
时间复杂度 (O(nlog 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;
}