题意:
给出长度为n的序列s1到sn,以及一个表达式 (si + sj) ^ sk的最大值,^代表异或运算,其中i,j,k不能相同。
思路:
一个典型的字典树处理异或和的问题,思路就是先将所有si按照二进制位从高到低拆成32位然后储存在字典树中,然后枚举i和j,让s[i]+s[j]在字典树上爬,每一位尽量往与当前位数相反的方向爬,这样最终结果这一位就是1,如果相反方向节点不存在,那么往相同方向爬,最终结果这一位就是0。另外为了保证下标不重复,每次枚举了i,j之后要先将i,j从字典树中删除,然后再计算结果更新最大值。
代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 1005;
const int maxbit = 35;
ll s[MAXN];
struct node {
node* Next[2];
ll cnt;
};
struct Trie {
node *root;
void init() { // 字典树初始化
root = new node;
for (int i = 0; i < 2; i++)
root->Next[i] = NULL;
}
void ins(ll x) { // 插入节点
node *p = root;
for (int i = maxbit; i >= 0; i--) {
int id = (x & (1LL << i)) ? 1 : 0;
if (p->Next[id] == NULL) {
node *q = new node;
for (int j = 0; j < 2; j++) q->Next[j] = NULL;
p->Next[id] = q;
p = p->Next[id];
p->cnt = 1;
}
else {
p = p->Next[id];
p->cnt++;
}
}
}
void del(ll x) { // 删除节点
node *p = root;
for (int i = maxbit; i >= 0; i--) {
int id = (x & (1LL << i)) ? 1 : 0;
p = p->Next[id];
p->cnt--;
}
}
ll cal(ll x) { // 在字典树上爬,计算最大异或和
node *p = root;
ll res = 0;
for (int i = maxbit; i >= 0; i--) {
int id = (x & (1LL << i)) ? 1 : 0;
res <<= 1;
if (p->Next[id ^ 1] != NULL && p->Next[id ^ 1]->cnt > 0) {
p = p->Next[id ^ 1];
res += 1;
}
else p = p->Next[id];
}
return res;
}
void cle(node *p) { // 清空字典树,回收空间
if (p == NULL) return;
if (p->Next[0] != NULL) cle(p->Next[0]);
if (p->Next[1] != NULL) cle(p->Next[1]);
free(p);
p = NULL;
}
} trie;
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n;
scanf("%d", &n);
trie.init();
for (int i = 1; i <= n; i++) {
scanf("%lld", &s[i]);
trie.ins(s[i]);
}
ll ans = 0;
for (int i = 1; i <= n; i++) {
trie.del(s[i]);
for (int j = i + 1; j <= n; j++) {
trie.del(s[j]);
ans = max(ans, trie.cal(s[i] + s[j]));
trie.ins(s[j]);
}
trie.ins(s[i]);
}
trie.cle(trie.root);
printf("%lld\n", ans);
}
return 0;
}