题意:给你一个序列a, 问a[i] ^ (a[j] & a[k])的最大值,其中i < j < k。
思路:我们考虑对于每个a[i]求出它的最优解。因为是异或运算,所以我们从高位向低位枚举,如果这一位a[i]是0,我们就在a[i]的右边找两个位置让它们按位与起来这位是1。那么,我们贪心的保留可以通过按位与凑出某个二进制数的最靠右的两个位置。这个可以通过dp的方式预处理出来。之后,我们枚举每一个数a[i],先找出它的哪些位是0,之后从高位到低位枚举,判断这一位是否可以变成1。如果之前已经加上的位再加上这一位可以被凑出来,那么我们就加上这一位。在所有的a[i]得出的答案中去最大值即可。
代码:
#include <bits/stdc++.h>
#define pii pair<int, int>
#define INF 0x3f3f3f3f
#define db double
using namespace std;
const int maxn = 2000010;
pii dp[1 << 21];
int a[maxn];
void update(int mask, int val) {
if(dp[mask].second == 0) {
dp[mask].second = val;
return;
}
if(dp[mask].first == val || dp[mask].second == val) return;
if(val > dp[mask].second) {
dp[mask].first = dp[mask].second;
dp[mask].second = val;
} else if(val > dp[mask].first) {
dp[mask].first = val;
}
}
void merge(int mask1, int mask2) {
if(dp[mask2].first != 0) update(mask1, dp[mask2].first);
if(dp[mask2].second != 0) update(mask1, dp[mask2].second);
}
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
update(a[i], i);
}
for (int i = 0; i < 21; i++) {
for (int j = 0; j < (1 << 21); j++) {
if((j >> i) & 1) {
merge(j ^ (1 << i), j);
}
}
}
int ans = 0;
for (int i = 1; i <= n; i++) {
int mask = 0, tmp = (1 << 21) - 1 - a[i];
for (int j = 20; j >= 0; j--) {
if((tmp >> j) & 1) {
if(dp[mask ^ (1 << j)].first > i && dp[mask ^ (1 << j)].second != 0) {
mask ^= (1 << j);
}
}
}
if(dp[mask].first > i && dp[mask].second != 0) ans = max(ans, a[i] ^ mask);
}
printf("%d\n", ans);
}