[BZOJ3166][Heoi2013]Alo(可持久化Trie)

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=3166

Solution

由于序列 a a 的每个元素不同,因此考虑对于每个 i 算出:
l l r 分别满足什么条件时,区间 [l,r] [ l , r ] 的次大值等于 ai a i
首先,预处理出 pre[i] p r e [ i ] 表示 i i 前面第一个比 ai 大的位置, nxt[i] n x t [ i ] 表示 i i 后面第一个比 ai 大的位置。
显然,当 l[pre[i]+1,i],r[i,nxt[i]1] l ∈ [ p r e [ i ] + 1 , i ] , r ∈ [ i , n x t [ i ] − 1 ] 时,区间 [l,r] [ l , r ] 的最大值为 ai a i
而如果要求 ai a i 为次大值,那么就要把 l l 往左移或者把 r 继续往右移。
所以,可利用 二分 + RMQ ,计算 [1,pre[i]] [ 1 , p r e [ i ] ] 中最右的比 ai a i 大的元素位置,和 [nxt[i],n] [ n x t [ i ] , n ] 中最左的比 ai a i 大的元素位置。
于是,我们得出了 pre2[i] p r e 2 [ i ] 表示 i i 前面第二个ai 大的位置, nxt2[i] n x t 2 [ i ] 表示 i i 后面第二个ai 大的位置。
于是得出,如果 [l,r] [ l , r ] 区间的次大值为 ai a i ,那么 l l r 满足:
(1) l[pre2[i]+1,pre[i]],r[i,nxt[i]1] l ∈ [ p r e 2 [ i ] + 1 , p r e [ i ] ] , r ∈ [ i , n x t [ i ] − 1 ] (如果 ai a i 前面没有比 ai a i 大的元素则不考虑此情况)
或(2) l[pre[i]+1,i],r[nxt[i],nxt2[i]1] l ∈ [ p r e [ i ] + 1 , i ] , r ∈ [ n x t [ i ] , n x t 2 [ i ] − 1 ] (如果 ai a i 后面没有比 ai a i 大的元素则不考虑此情况)
当然,我们要求最大异或,因此要让区间范围尽可能大。
因此只需要考虑区间 [pre2[i]+1,nxt[i]1] [ p r e 2 [ i ] + 1 , n x t [ i ] − 1 ] [pre[i]+1,nxt2[i]1] [ p r e [ i ] + 1 , n x t 2 [ i ] − 1 ] 即可。
于是,问题变成了:
多组询问,每次给出一个区间 [l,r] [ l , r ] 和定值 x x ,在区间 [l,r] 中选一个数,使这个数与 x x <script type="math/tex" id="MathJax-Element-4651">x</script> 的异或值最大。
这是可持久化 Trie 的经典应用。

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 5e4 + 5, M = 2e6 + 5, LogN = 20;
int n, QAQ, a[N], rt[N], stk[N], pre[N], nxt[N], top, RMQ[N][LogN], Log[N];
struct cyx {int lc, rc, id;} T[M];
void ins(int y, int &x, int val, int d, int p) {
    T[x = ++QAQ] = T[y]; T[x].id = max(T[x].id, p); if (d == -1) return;
    if ((val >> d) & 1) ins(T[y].rc, T[x].rc, val, d - 1, p);
    else ins(T[y].lc, T[x].lc, val, d - 1, p);
}
int qmax(int l, int r) {
    int x = Log[r - l + 1]; return max(RMQ[l][x], RMQ[r - (1 << x) + 1][x]);
}
int querymax(int l, int r, int x) {
    int i, u = rt[r], ans = 0; Rof (i, 30, 0) {
        int y = (x >> i) & 1;
        if (y)
            u = T[u].lc && T[T[u].lc].id >= l ? T[u].lc : (ans |= 1 << i, T[u].rc);
        else u = T[u].rc && T[T[u].rc].id >= l ? (ans |= 1 << i, T[u].rc) : T[u].lc;
    }
    return ans ^ x;
}
int main() {
    int i, j, ans = 0; n = read(); For (i, 1, n) a[i] = read();
    For (i, 1, n) ins(rt[i - 1], rt[i], a[i], 30, i);
    stk[top = 0] = 0; For (i, 1, n) {
        while (top && a[stk[top]] < a[i]) top--;
        pre[stk[++top] = i] = stk[top - 1];
    }
    stk[top = 0] = n + 1; Rof (i, n, 1) {
        while (top && a[stk[top]] < a[i]) top--;
        nxt[stk[++top] = i] = stk[top - 1];
    }
    Log[0] = -1; For (i, 1, n) Log[i] = Log[i >> 1] + 1;
    For (i, 1, n) RMQ[i][0] = a[i]; For (j, 1, 16) For (i, 1, n - (1 << j) + 1)
        RMQ[i][j] = max(RMQ[i][j - 1], RMQ[i + (1 << j - 1)][j - 1]);
    For (i, 1, n) {
        if (pre[i] >= 1) {
            int l = 1, r = pre[i] - 1, mid;
            while (l <= r) {
                int mid = l + r >> 1;
                if (qmax(mid, pre[i] - 1) > a[i]) l = mid + 1;
                else r = mid - 1;
            }
            ans = max(ans, querymax(r + 1, nxt[i] - 1, a[i]));
        }
        if (nxt[i] <= n) {
            int l = nxt[i] + 1, r = n, mid;
            while (l <= r) {
                int mid = l + r >> 1;
                if (qmax(nxt[i] + 1, mid) > a[i]) r = mid - 1;
                else l = mid + 1;
            }
            ans = max(ans, querymax(pre[i] + 1, l - 1, a[i]));
        }
    }
    cout << ans << endl; return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值