您的位置:首页 > 其它

CF 665E 限制异或和的区间计数

2016-05-04 21:45 274 查看

题意

原题链接: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 和 Sj 以后,Si 必须满足一些模式

举一个具体的例子,k=10101,Sj=10000,有以下的Si符合条件

S(1)i=01xxxS(2)i=0011xS(3)i=00101

为了找出所有这些模式,只需要从高位到低位扫描,逐位比较 k 和 Sj。

例如,比较k 和 Sj的次高位

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

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ACM CF 异或和 字典树 Trie