【模板】线性基
题目链接:luogu P3812
题目大意
给你一些数,要你选任意个数,使得它们的异或值最大。
输出这个最大的异或值。
思路
线性基
线性基是什么呢?
它经常用于解决异或的问题,如果原本数组值域为
[
1
,
n
]
[1,n]
[1,n],那线性基大小就是
[
1
,
l
o
g
2
n
]
[1,log_2n]
[1,log2n]。
它是一个数组,其中你原本数组的数都可以由它里面的一些数异或得到。
那根据这个性质,我们就可以解决许多异或的问题。
如何构造
设现在构造出的线性基数组是
p
i
p_i
pi。
对于一个新要放进原本数组的数
x
x
x,我们从二进制高位向低位枚举。
如果碰到第
i
i
i 位是
1
1
1,那我们就做判断,如果
p
i
=
0
p_i=0
pi=0,那就直接把
x
x
x 作为
p
i
p_i
pi。
那如果不是
0
0
0,就把
x
x
x 异或上
p
i
p_i
pi 继续判断。
如何求异或最大值
容易想到贪心。
就尽可能让高位是
1
1
1。
那我们从高位向低位枚举线性基,如果当前答案这一位是
0
0
0,那就异或上它。(让它变成
1
1
1)
代码
#include<cstdio>
#define ll long long
using namespace std;
int n;
ll x, p[51];
void add(ll x) {//插入数
for (int i = 50; i >= 0; i--)
if ((x >> i) & 1) {
if (!p[i]) {
p[i] = x;
break;
}
x ^= p[i];
}
}
ll askmax() {
ll re = 0;
for (int i = 50; i >= 0; i--)
if (!((re >> i) & 1))//尽量让最高位的为 1
re ^= p[i];
return re;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld", &x);
add(x);
}
printf("%lld", askmax());
return 0;
}