Description
现在你拥有 (n) 颗宝石,每颗宝石有一个能量密度,记为 (a_i),这些宝石的能量密度两两不同。现在你可以选取连续的一些宝石(必须多于一个)进行融合,设为 (a_i, a_{i+1}, cdots, a_j),则融合而成的宝石的能量密度为这些宝石中能量密度的次大值与其他任意一颗宝石的能量密度按位异或的值,即:设该段宝石能量密度次大值为 (k),则生成的宝石的能量密度为 (max {k ext{xor} a_p mid a_p eq k, i leq p leq j })。
现在你需要知道你怎么选取需要融合的宝石,才能使生成的宝石能量密度最大。
数据范围:(1 leq n leq 10^5),(1 leq a_i leq 10^9)。
Solution
枚举宝石融合的区间显然时间会爆炸,那么考虑枚举宝石融合的区间中的次大值。
设当前枚举的到的次大值在序列中的位置为 (i)。
设 ([1, i)) 中从右往左数第一个大于等于 (a_i) 的数的位置为 (L_1),第二个大于等于 (a_i) 的数的位置为 (L_2)。
设 ((i, n]) 中从左往右数第一个大于等于 (a_i) 的数的位置为 (R_1),第二个大于等于 (a_i) 的数的位置为 (R_2)。
注意到要使得 (a_i) 成为区间中的非严格次大值。
必须要令该区间 " 恰好包含一个大于 (a_i) 的数 " 或者 " 该区间的最大值为 (a_i) "
主要关注的是第一种情况,那么我们要令 (l in (L_2, L_1], r in [i, R_1)) 或 (l in (L_1, i], r in [R_1, R_2))。在符合这样条件的区间下的次大值都可以取到 (a_i)。
注意到最后答案的形式是 (a_i ext{xor} a_p) 的形式,在固定 (a_i) 的情况下,我们需要选一个点 (p) 使得 (a_i ext{xor} a_p) 最大。
那么 (p) 包含在上述任意一个符合条件的区间中即可,也就是 (p) 被包含在上述的所有符合条件的区间并。
即 (p in (L_2, R_2))。
那么对于每个 (i),我们要求出 (maxlimits_{L_2 < p < R_2} left{ a_i ext{xor} a_p ight}) 的值,可以使用可持久化 trie 轻松做到。
至于要求出每个点 (i) 对应的 (L_2, R_2),考虑按 (a_i) 从小到大处理每个宝石,使用双向链表维护前驱后继,处理完了每个 (a_i) 后实时删除即可。
时间复杂度 (mathcal{O}(n log ext{SIZE})),其中 ( ext{SIZE}) 表示值域大小。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
inline int read() {
int x = 0, f = 1; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -f; s = getchar(); }
while (s >= '0' && s <= '9') { x = x * 10 + s - '0'; s = getchar(); }
return x * f;
}
const int N = 50100;
int n;
int a[N];
int cT, root[N];
struct trie {
int ch[2];
int cnt;
} t[N * 32];
void insert(int &p, int now, int dep, int val) {
p = ++ cT;
t[p] = t[now];
t[p].cnt ++;
if (dep < 0) return;
int v = val >> dep & 1;
insert(t[p].ch[v], t[now].ch[v], dep - 1, val);
}
int ask(int p, int q, int dep, int val) {
if (dep < 0) return 0;
int v = val >> dep & 1;
int cnt = t[t[q].ch[v ^ 1]].cnt - t[t[p].ch[v ^ 1]].cnt;
if (cnt)
return ask(t[p].ch[v ^ 1], t[q].ch[v ^ 1], dep - 1, val) + (1 << dep);
else
return ask(t[p].ch[v], t[q].ch[v], dep - 1, val);
}
int H[N];
bool cmp(int i, int j) {
return a[i] < a[j];
}
int Prev[N], Next[N];
int main() {
n = read();
for (int i = 1; i <= n; i ++)
a[i] = read();
for (int i = 1; i <= n; i ++)
insert(root[i], root[i - 1], 30, a[i]);
for (int i = 1; i <= n; i ++) H[i] = i;
sort(H + 1, H + 1 + n, cmp);
for (int i = 1; i <= n; i ++) Prev[i] = i - 1, Next[i] = i + 1;
Next[n + 1] = n + 1;
int ans = 0;
for (int i = 1; i <= n; i ++) {
int id = H[i];
int l = max(1, Prev[Prev[id]] + 1), r = min(n, Next[Next[id]] - 1);
ans = max(ans, ask(root[l - 1], root[r], 30, a[id]));
Prev[Next[id]] = Prev[id], Next[Prev[id]] = Next[id];
}
printf("%d
", ans);
return 0;
}