题目描述:
给定一个长度为 (N) 的序列 ((N le 10^5))。
要你把这个序列划分成两个集合。
最后答案等于第一个集合中所有元素的异或和((operatorname{xor}))加上第二个集合中所有元素的异或和。
最大化答案。
正解
一般看到位运算都会要先去把每一位分开考虑。
假如这一位有奇数个 1,那么无论怎么拆分成两个集合这一位始终有 1 的贡献。
所以先把奇数个 1 的贡献给算出来,不加以考虑。
假如这一位有偶数个 1,那么这一位的所有数异或起来结果肯定是 0。
设分成两个集合之后,两个集合不考虑奇数个 1 的位个话,异或和分别为 (x_1), (x_2)。
就有 (x_1 operatorname{xor}x_2) 等于 0,也就是说 (x_1=x_2)。
那么只要让其中一个数最大,另一个数会等于这个数,答案也就是最大的。
这一步可以用线性基做,时间复杂度(O(nlog n))。
[ans = 2 imes x_{max} + sum_{i = 0}^{60} [cnt_i mod 2 = 1] imes2^i
]
(color{DeepSkyBlue} {Code :})
#include <bits/stdc++.h>
#define MAX 100005
#define bit(x) (1ll << x)
using namespace std;
int n;
int cnt[64];
long long a[MAX], ans;
inline long long Rd() {
long long x = 0; char ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
return x;
}
struct linear_base {
long long u[60];
inline void insert(long long val) {
for(int i = 59; i >= 0; --i) {
if(val & bit(i)) {
if(u[i]) val ^= u[i];
else {
u[i] = val;
return;
}
}
}
}
inline long long query() {
long long res = 0;
for(int i = 59; i >= 0; --i)
if((res ^ u[i]) > res) res ^= u[i];
return res;
}
}lsk;
int main() {
n = Rd();
for(int i = 1; i <= n; ++i) {
a[i] = Rd();
for(int j = 59; j >= 0; --j)
if(a[i] & bit(j)) ++cnt[j];
}
for(int i = 59; i >= 0; --i) {
if(cnt[i] & 1) {
ans += bit(i);
for(int j = 1; j <= n; ++j) {
if(a[j] & bit(i)) {
a[j] ^= bit(i);
}
}
}
}
for(int i = 1; i <= n; ++i) lsk.insert(a[i]);
cout << ans + 2ll * lsk.query() << endl;
return 0;
}