[高维前缀和(SOS DP)] Codeforces 1208F BITS AND PIECES
题目大意
给定一个长为 \(n(3\leq n\leq 10^6)\) 的数组 \(\{a_n\}\),其中 \(0\leq a_i\leq 2\times 10^6\),对于所有的 \(1\leq i< j<k\leq n\),求 \(\max\{a_i|(a_j \& a_k)\}\)。
题解
首先我们可以枚举 \(a_i\) ,对于所有相等的 \(a_i\),取下标 \(i\) 最小的一个。然后对于这个 \(a_i\),我们从二进制的高位到低位去考虑,若 \(a_i\) 这一位上为 \(0\) ,我们要去找是否存在 \(a_j\&a_k\) 的这一位上是 \(1\),且 \(i<j<k\),这样 \(a_j\&a_k\) 按位或上 \(a_i\) 能使得这一位为 \(1\)。我们并不需要去枚举 \(a_i \& a_j\),记 \(a_i\& a_j=x\),则只需要在 \(x\) 的超集(\(x\subset y\))中寻找即可。我们可以用高维前缀和维护 \(x\) 的所有超集中最大的和次大的下标,如果次大的下标大于 \(i\),则说明存在这样的一组 \(i,j,k\),更新答案取 \(\max\) 即可。时间复杂度 \(O(n\log n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
template<typename elemType>
inline void Read(elemType& T) {
elemType X = 0, w = 0; char ch = 0;
while (!isdigit(ch)) { w |= ch == '-';ch = getchar(); }
while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
T = (w ? -X : X);
}
int a[2000010], f[2000010][2];
int n;
int main() {
Read(n);
int mx = 0, ans = 0;
for (int i = 1;i <= n;++i) {
int x;Read(x);
if (!a[x]) a[x] = i;
mx = max(mx, x);
f[x][1] = f[x][0];
f[x][0] = i;
if (i <= n - 2) ans = max(ans, x);
}
int m = 0; while ((1 << m) < mx) ++m;
if ((1 << m) == mx) ++m;
for (int i = 0;i < m;++i) {
for (int j = 0;j < (1 << m);++j) {
if (!(j & (1 << i))) {
if (f[j ^ (1 << i)][0] > f[j][0]) {
f[j][1] = max(f[j][0], f[j ^ (1 << i)][1]);
f[j][0] = f[j ^ (1 << i)][0];
}
else if (f[j ^ (1 << i)][0] != f[j][0] && f[j ^ (1 << i)][0] > f[j][1])
f[j][1] = f[j ^ (1 << i)][0];
}
}
}
for (int i = 0;i < (1 << m);++i) {
if (!a[i]) continue;
int x = 0;
for (int j = m - 1;j >= 0;--j) {
if (!(i & (1 << j))) {
if (f[x | (1 << j)][1] > a[i]) {
x |= (1 << j);
ans = max(ans, i | x);
}
}
}
}
printf("%d\n", ans);
return 0;
}