题目地址
题意
共有 T T T组数据,每组数据都给定一个长度为 n n n的整数序列,然后从里面选出来三个数 a i , a j , a k a_i,a_j,a_k ai,aj,ak,要求 ( a i + a j ) ⊕ a k (a_i+a_j)\oplus a_k (ai+aj)⊕ak的值最大。
数据范围
3 ≤ n ≤ 1000 3 \le n \le 1000 3≤n≤1000
1 ≤ T ≤ 1000 1 \le T \le 1000 1≤T≤1000
0 ≤ a i ≤ 1 0 9 0 \le a_i \le 10^9 0≤ai≤109
思路
Trie树有一道经典例题,是一个整数序列,然后选择两个数令其异或和最大。先回顾一下那道题的思路:我们将所有的数字全部插入Trie树中,然后我们循环遍历所有的数字,求出最大异或和。
这道题在例题的基础上增加了一些难度,题目要求从数列1中选出两个数相加后与第三个数异或,求所选数字组成的最大值。
如果这道题用暴力来写的话,时间复杂度就是 O ( n 3 ) O(n^3) O(n3) 妥妥超时,但是只需降低一个时间复杂度,令时间复杂度降为 O ( n 2 l o g n ) O(n^2logn) O(n2logn)就不会超时了。因此我们考虑优化暴力的思路。
由于需要选出来两个数字相加,所以我们需要用两层循环枚举两个相加的数字,然后在枚举第三个数字时,我们可以利用上面例题的思路,使用01Trie进行求解。在暴力枚举完两个相加的数字之后,剩下的操作就和上面的例题一模一样。
例题地址
实现代码(c++)
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
#define LL long long
#define ULL unsigned long long
#define PII pair<int, int>
#define lowbit(x) (x & -x)
#define Mid ((l + r) >> 1)
#define ALL(x) x.begin(), x.end()
#define endl '\n'
#define fi first
#define se second
const int INF = 0x7fffffff;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int n, a[N], res;
//val数组记录异或和,s数组记录该节点出现的次数
int t[N][2], s[N], val[N], cnt;
void Insert(int x) {
int p = 0;
for(int i = 30; i >= 0; i -- ) {
int &u = t[p][x >> i & 1];
if(!u) {
u = ++ cnt;
memset(t[cnt], 0, sizeof t[cnt]);
s[cnt] = val[cnt] = 0;
}
p = u, s[p] ++;
}
val[p] = x;
}
void Delete(int x) {
int p = 0;
for(int i = 30; i >= 0; i -- ) {
int u = x >> i & 1;
p = t[p][u];
//记录节点p出现的次数
-- s[p];
}
}
int Query(int x) {
int p = 0;
for(int i = 30; i >= 0; i -- ) {
int u = x >> i & 1;
if(t[p][!u] && s[t[p][!u]]) p = t[p][!u];
else p = t[p][u];
}
return x ^ val[p];
}
signed main(void) {
IOS
int ALL; cin >> ALL;
while(ALL -- ) {
cin >> n;
res = 0; cnt = 0; memset(t, 0, sizeof t);
//读入整数序列,同时将其插入Trie
for(int i = 1; i <= n; i ++ ) cin >> a[i], Insert(a[i]);
for(int i = 1; i <= n; i ++ ) {
//将枚举的数字从Trie上删除
Delete(a[i]);
for(int j = i + 1; j <= n; j ++ ) {
Delete(a[j]);
res = max(res, Query(a[i] + a[j]));
Insert(a[j]);
}
//删除后记得加上
Insert(a[i]);
}
cout << res << endl;
}
return 0;
}