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; }