题意:
将所给的 n n n 个数分为两个非空的集合 A A A 和 B B B,要求满足: A A A中所有元素的异或值 + + + B B B中所有元素的异或值,最后得到的和的值最大。
思路:
一堆数的异或值,不难想到线性基,难点是要怎么用。
第一个比较难想的点是,我们需要先求出来所有数的异或和
s
u
m
sum
sum,或者换句话说,我们需要知道这
n
n
n 个数转换到个二进制位之后,每一个二进制位
1
1
1 的个数。
假设,在某一个二进制位,
(
s
u
m
>
>
i
)
&
1
(sum>>i )\&1
(sum>>i)&1 的值为
1
1
1,这意味着无论怎么分组,两个集合的异或值
s
u
m
A
sumA
sumA 和
s
u
m
B
sumB
sumB 在这一个二进制位上必定是一个
0
0
0 和一个
1
1
1,而在最终得到的值
a
n
s
ans
ans 中,这一个二进制位上的值也就必定是
1
1
1。
而在 ( s u m > > i ) & 1 (sum>>i )\&1 (sum>>i)&1 的值为 0 0 0 的二进制位上,我们当然希望 s u m A sumA sumA 和 s u m B sumB sumB 在这一位上均为 1 1 1,这样可以使最终值更大。那么就从高位到低位遍历,然后贪心即可。
这里还有一个细节,去除
(
s
u
m
>
>
i
)
&
1
(sum>>i )\&1
(sum>>i)&1 的值为
1
1
1的二进制位的影响之后,因为
s
u
m
A
⊕
s
u
m
B
=
s
u
m
sumA\oplus sumB=sum
sumA⊕sumB=sum,然后
s
u
m
A
sumA
sumA 和
s
u
m
B
sumB
sumB 每一个二进制位不是都为
0
0
0 就都为
1
1
1,也就是说,
s
u
m
A
=
s
u
m
B
sumA=sumB
sumA=sumB (这里说的单指去掉值为
1
1
1 的二进制位之后的值),那么只需要贪心求出线性基中任意元素异或的最大值即可。
注意,这里所有的操作都建立在去除
(
s
u
m
>
>
i
)
&
1
(sum>>i )\&1
(sum>>i)&1 的值为
1
1
1的二进制位的影响之后,因此原数组中的数在插入线性基之前,必须去除相应二进制位上的值。
时间复杂度: O ( n ) O(n) O(n)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10, M = 350, mod = 1e9 + 7;
#define LL long long
int n;
LL s[N];
LL p[110];
void add(LL x) {
for (int i = 62; i >= 0; i--) {
if (!(x >> i))
continue;
if (!p[i]) {
p[i] = x;
break;
}
x ^= p[i];
}
return ;
}
signed main() {
cin >> n;
LL sum = 0;
for (int i = 1; i <= n; i++)
scanf("%lld", &s[i]), sum ^= s[i];
for (int i = 62; i >= 0; i--)
if (sum >> i & 1ll) {
for (int j = 1; j <= n; j++)
if (s[j] >> i & 1ll)
s[j] ^= (1ll << i);
}
for (int i = 1; i <= n; i++)
add(s[i]);
LL ans = 0;
for (int i = 62; i >= 0; i--)
if ((ans ^ p[i]) > ans)
ans ^= p[i];
cout << (ans ^ sum) + ans;
return 0;
}