題解:[SCOI2016] 美味

Avalaunch發表於2024-11-12

前置知識:可持久化線段樹(主席樹)

洛谷3293 [SCOI2016] 美味

問題

有一個長度為 \(n\) 的序列 \(a_1,a_2,...,a_n\)。每次詢問給你 \(b\)\(x\),你需要求出 \(\max\{a_i+x \bigoplus b\}\)

\(1 \le l \le r \le n \le 2\times 10^5, 0 \le a_i,b,x < 10^5\)

首先,有 \(l,r\) 應該要可持久化。然後……01trie?行不通啊。

沒有頭緒的話,先來看一道比較經典的題。

經典の問題

有一個長度為 \(n\) 的數列 \(a_1,a_2,...,a_n\)。有 \(m\) 次詢問,給你一個數 \(k\),求出 \(k \bigoplus a_i\) 最大的 \(a_i\)
\(1 \le n,m,a_i \le 10^5\)

你肯定知道這是 01Trie 板子,但是我想介紹另一個東西——

Technology

還是考慮貪心,從高位往低位貪。

假設 \(w_x(i)\) 表示數 \(x\) 二進位制下的第 \(i\) 位,當前的答案(即確定了前幾位)是 \(ans\)

以一個例子來說明:

  k:  01100100101
ans:  1010???????

假如我們當前已經考慮完了前 \(4\) 位(從高往低),那我們肯定想要讓 \(w_{ans}(5) \ne w_k(5)\),即 \(w_{ans}(5) = 1\)。那我們怎麼知道是否存在這樣的 \(ans\)?顯然,此時需要滿足的是:\((10101000000)_2 \le ans \le (10101111111)_2\)。權值線段樹上查即可。

那麼再來看這個題大家就都會做了,把範圍再縮小 \(x\) 就行。由於需要限制區間,將權值線段樹可持久化即可。

程式碼

#include <bits/stdc++.h>
using namespace std;
#define lson son[u][0]
#define rson son[u][1]
const int N = 2e5 + 5, ND = N << 6;
struct segtree
{
    int tot, son[ND][2], cnt[ND];
    void pushup(int u)
    {
        cnt[u] = cnt[lson] + cnt[rson];
    }
    int update(int v, int l, int r, int it)
    {
        int u = ++tot;
        if (l == r)
        {
            cnt[u] = cnt[v] + 1;
            return u;
        }
        lson = son[v][0], rson = son[v][1];
        int mid = (l + r) >> 1;
        if (it <= mid)
            lson = update(son[v][0], l, mid, it);
        else
            rson = update(son[v][1], mid + 1, r, it);
        pushup(u);
        return u;
    }
    int query(int u, int l, int r, int L, int R)
    {
        if (L <= l && r <= R)
            return cnt[u];
        if (R < l || r < L)
            return 0;
        int mid = (l + r) >> 1;
        return query(lson, l, mid, L, R) + query(rson, mid + 1, r, L, R);
    }
} t;
int n, Q, a[N], root[N];
/*
if b_i == 0
    a in [ans + (1 << i) - x, ans + (1 << i + 1) - 1 - x]
else
    a in [ans - x, ans + (1 << i) - 1 - x]
*/

int main()
{
#ifdef aquazhao
    freopen("data.in", "r", stdin);
    freopen("data.out", "w", stdout);
#endif
    cin >> n >> Q;
    for (int i = 1; i <= n; i++)
        scanf("%d", a + i), root[i] = t.update(root[i - 1], 0, 1e5, a[i]);
    int b, x, l, r;
    while (Q--)
    {
        scanf("%d%d%d%d", &b, &x, &l, &r);
        int ans = 0;
        for (int i = 17; i >= 0; i--)
        {
            int cnt;
            if (!(b & (1 << i)))
            {
                int L = max(0, ans + (1 << i) - x), R = min((int)1e5, ans + (1 << (i + 1)) - 1 - x);
                cnt = t.query(root[r], 0, 1e5, L, R) - t.query(root[l - 1], 0, 1e5, L, R);
                ans += cnt > 0 ? 1 << i : 0;
            }
            else
            {
                int L = max(0, ans - x), R = min((int)1e5, ans + (1 << i) - 1 - x);
                cnt = t.query(root[r], 0, 1e5, L, R) - t.query(root[l - 1], 0, 1e5, L, R);
                ans += cnt > 0 ? 0 : 1 << i;
            }
        }
        printf("%d\n", b ^ ans);
    }
    return 0;
}

相關文章