前置知識:可持久化線段樹(主席樹)
洛谷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;
}