可以利用二分的思想,具体步骤,就是从高到低确定每一个二进制位的数值。
可以看出,如果当前已经确定了前几位,那么这个解的范围一定是一个连续的区间。举个例子,假设初始解的枚举范围是
[0,262143]
(即
18
位二进制数),如果当前已经确定了第
18,17,16
位分别为
1,0,1
,那么这时候解的范围就是
[163840,196607]
。
设当前解的范围是
[s,t]
,第
i+1
位到第
18
位已经被确定,现在考虑怎样确定第
i
位,也就是说判断要把解的范围缩小为
然后可以继续发现,
[L,R]
内的数被
xor
上
b
之后得到的值域,仍然是一段连续的区间。这时候,由于
可以得出:
u=⌊L xor b2i−1⌋∗2i−1
v=⌊R xor b2i−1⌋∗2i−1+2i−1−1
这时候就可以看出,所有美味度为
[L,R]
的菜,都是评价值在区间
[u−x,v−x]
内的菜。如果在给定的可选择菜品区间内,存在一道菜的评价值在区间
[u−x,v−x]
内,那么就可以把答案区间调整为
[s+t2+1,t]
,否则调整为
[s,s+t2]
。
对于询问一个可选择菜品区间内评价值在区间
[u−x,v−x]
内的菜的个数,可以用树状数组离线询问。但是由于询问之间有联系,所以用主席树代替树状数组,以达到在线查询的目的。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
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 = 2e5 + 5, NLogN = 7e6 + 5;
struct cyx {
int lc, rc, cnt;
} T[NLogN];
int n, m, a[N], QAQ, rt[N];
void ins(int y, int &x, int l, int r, int p) {
T[x = ++QAQ] = T[y]; T[x].cnt++; int mid = l + r >> 1;
if (l == r) return; if (p <= mid) ins(T[y].lc, T[x].lc, l, mid, p);
else ins(T[y].rc, T[x].rc, mid + 1, r, p);
}
int query(int l, int r, int s, int e, int p1, int p2) {
if (l == s && r == e) return T[p2].cnt - T[p1].cnt;
int mid = l + r >> 1;
if (e <= mid) return query(l, mid, s, e, T[p1].lc, T[p2].lc);
else if (s >= mid + 1) return query(mid + 1, r, s, e, T[p1].rc, T[p2].rc);
else return query(l, mid, s, mid, T[p1].lc, T[p2].lc) +
query(mid + 1, r, mid + 1, e, T[p1].rc, T[p2].rc);
}
void init() {
int i; for (i = 1; i <= n; i++)
ins(rt[i - 1], rt[i], 0, 131071, a[i]);
}
int main() {
int i, b, x, l, r, ans; n = read(); m = read();
for (i = 1; i <= n; i++) a[i] = read(); init();
while (m--) {
b = read(); x = read(); l = read(); r = read();
ans = 0; for (i = 17; i >= 0; i--) {
int tl = ans | (1 << i), tr = tl + (1 << i) - 1;
tl ^= b; tr ^= b; tl = (tl >> i) << i; tr = (tr >> i) << i;
tr += (1 << i) - 1; tl -= x; tr -= x;
if (tr < 0 || tl > 131071) continue;
if (tl < 0) tl = 0; if (tr > 131071) tr = 131071;
if (query(0, 131071, tl, tr, rt[l - 1], rt[r]))
ans = ans | (1 << i);
}
printf("%d\n", ans);
}
return 0;
}