给出一个长度1e5的数组, 1e5次询问,
l
l
l到
r
r
r之间异或值为
k
k
k的区间个数.
这个题的思想和那个"求区间和为0的最长区间长度"的题很像.
若
a
∧
b
=
c
a \wedge b=c
a∧b=c, 那么
a
∧
c
=
b
a \wedge c=b
a∧c=b.
先对a数组计算前缀异或和sum.
设
∧
(
a
[
l
]
.
.
.
a
[
r
]
)
=
k
\wedge(a[l]...a[r])=k
∧(a[l]...a[r])=k, 即
s
u
m
[
r
]
∧
s
u
m
[
l
−
1
]
=
k
sum[r]\wedge sum[l-1]=k
sum[r]∧sum[l−1]=k.
假如当前已经计算出区间
i
i
i到
j
j
j中, 异或值为
k
k
k的区间个数
a
n
s
ans
ans.
现在我们要新增一个数
j
+
1
j+1
j+1.
新增一个数答案不可能更坏, 也就是说新的
a
n
s
ans
ans是在当前
a
n
s
ans
ans上加上
j
+
1
j+1
j+1的贡献.
那么
j
+
1
j+1
j+1的贡献怎么算? 其实就是
i
i
i到
j
j
j之间的数, 有多少个会和
j
+
1
j+1
j+1"配对"(满足题意
∧
(
a
[
x
]
.
.
.
a
[
j
+
1
]
)
=
k
\wedge (a[x]...a[j+1])=k
∧(a[x]...a[j+1])=k).
即
s
u
m
[
x
]
∧
s
u
m
[
j
+
1
]
=
k
sum[x]\wedge sum[j+1]=k
sum[x]∧sum[j+1]=k;
即
s
u
m
[
j
+
1
]
∧
k
=
s
u
m
[
x
]
sum[j+1]\wedge k=sum[x]
sum[j+1]∧k=sum[x].
这是什么意思呢? 对于
i
i
i到
j
j
j里任何一个
x
x
x, 只需要
s
u
m
[
x
]
=
s
u
m
[
j
+
1
]
∧
k
sum[x]=sum[j+1]\wedge k
sum[x]=sum[j+1]∧k, 那么它就能和
j
+
1
j+1
j+1配对!
那么我们自然想到维护
i
i
i到
j
j
j内,每个
s
u
m
sum
sum值的出现的次数. 这样在每来一个新的
j
+
1
j+1
j+1, 我们就能
O
(
1
)
O(1)
O(1)算出它的贡献.
如果要从区间中去掉头/尾, 其实道理和新增是一样的.
显然就是莫队了.
p.s.
因为询问的是l到r的区间内有多少个子区间异或和为k, 而我们真正的操作是查询l-1到r之间的前缀异或和, 所以l要-1.
代码:
const int M = 2123456;// 2e5+5
int a[M], n, q, k, blo[M], bloSZ;
ll answer, ans[M];
struct query {
int l, r, id;
} queries[M];
bool cmp(query x, query y) {return (blo[x.l] ^ blo[y.l]) ? blo[x.l] < blo[y.l] : ((blo[x.l] & 1) ? x.r < y.r : x.r > y.r);}
void divide() {
bloSZ = sqrt(n);
for (int i = 1; i <= n; ++i) {
blo[i] = (i - 1) / bloSZ + 1;
}
}
ll cnt[M];
void add(int pos) {
answer += cnt[a[pos] ^ k];
cnt[a[pos]]++;
}
void del(int pos) {
cnt[a[pos]]--;
answer -= cnt[a[pos] ^ k];
}
void Mo() {
sort(queries + 1, queries + 1 + q, cmp);
int l = 0, r = -1;
for (int i = 1; i <= q; ++i) {
while (l > queries[i].l)add(--l);
while (r < queries[i].r)add(++r);
while (l < queries[i].l)del(l++);
while (r > queries[i].r)del(r--);
ans[queries[i].id] = answer;
}
}
void init() {
n = read(), q = read(), k = read();
for (int i = 1; i <= n; ++i) {
a[i] = read();
a[i] = a[i - 1] ^ a[i];
}
divide();
for (int i = 1; i <= q; ++i) {
int l = read(), r = read();
l--;
queries[i] = {l, r, i};
}
Mo();
for (int i = 1; i <= q; ++i)write(ans[i]), enter;
}