题意
原题链接:Beautiful Subarrays
长为 n 的正整数序列
A ,它的子区间异或和记为
Si,j=Ai⊕Ai+1⊕⋯⊕Aj
给定 k , 统计Si,j≥k 的子区间个数.
解法
Tags: 异或前缀和、字典树
记 Si=S1,i ,区间异或和满足以下性质
Si,j=Sj⊕Si−1
问题可转化为:对任意一个 Sj ,找出所有的 i<j ,满足 Sj⊕Si≥k .
确定了
k
和
举一个具体的例子,
k=10101,Sj=10000
,有以下的
Si
符合条件
S(1)i=01xxxS(2)i=0011xS(3)i=00101
为了找出所有这些模式,只需要从高位到低位扫描,逐位比较 k 和
例如,比较 k 和
k[0..1]=10Sj[0..1]=10若有Si[0..1]=01,必有Si⊕Sj≥k得模式串S(1)i=01xxx
为了统计符合这种模式的
Si
的数目,可以利用字典树(Trie)。
先将
Sj
的二进制表示串插入Trie中。对于Trie的每个点
Q
,记录
countQ=以Q点为根的叶子节点的数目
插入 Sj 时,顺带更新树上路径的 countQ 。
有了 countQ ,就可以根据 Si 的模式,统计满足条件的 Si 的数目了。
时间复杂度: O(32∗n) ,32是int的比特数
Implementation Tips:
- 不必一一遍历 Si 的模式,除最后一位外,他们有相同前缀
- 插入 A0=0
Source:
#include <bits/stdc++.h>
using namespace std;
#define log(x) cerr << __LINE__ << "#\t" << #x << "= " << (x) << endl
const int maxn = 1000010, maxb = 32;
struct Trie {
int c[2] , cnt;
Trie() {
cnt = 0;
c[0] = c[1] = -1;
}
} h[maxn * maxb];
int hc = 1, n, k, a[maxn];
typedef long long ll;
void insert(int x) {
int p = 0;
for(int i =31; i >= 0; i--) {
bool b = (x >> i) & 1;
if(h[p].c[b] == -1) {
h[p].c[b] = hc;
p = hc++;
}
else {
p = h[p].c[b];
}
h[p].cnt ++;
}
}
int search(int x) {
int p = 0, rnt = 0;
for(int i = 31; i>=0 && p != -1; i--) {
bool b2 = (x >> i) & 1, b1 = (k >> i) & 1;
if(b1 == 0 && b2 == 1 && h[p].c[!b2] != -1) {
rnt += h[h[p].c[!b2]].cnt;
//cout << "!" << endl;
}
if(b1 == 0 && b2 == 0 && h[p].c[!b2] != -1) {
rnt += h[h[p].c[!b2]].cnt;
}
p = h[p].c[b2];
}
if(p != -1)
rnt += h[p].cnt;
return rnt;
}
int main()
{
ll ans = 0;
cin >> n >> k;
insert(0);
int acc = 0;
for(int i = 0; i < n; i++) {
scanf("%d", &a[i]);
acc ^= a[i];
insert(acc);
ans += search(acc^k);
}
cout << ans << endl;
return 0;
}