CODEFORCES Educational Round 12 E. Beautiful Subarrays

http://codeforces.com/contest/665/problem/E

给出 n 个整数 a[1], a[2], ..., a[n]。 问有多少区间的亦或和大于等于 k。

Input

n k
a[1] a[2] ... a[n]

1 ≤ n ≤ 1e6
1 ≤ k ≤ 1e9
0 ≤ a[i] ≤ 1e9

Output

亦或和大于等于 k 的区间个数。

Examples

Case 1

3 1
1 2 3
5

Case 2

3 2
1 2 3
3

Case 3

3 3
1 2 3
2

Solution

处理前缀亦或和 s[i]。转化选择两个数 s[i] 和 s[j] (0 <= i < j <= n),使得 s[i] ^ s[j] >= k。这是个经典问题,使用将每个数看做是 31 位比特串(最高位看做第一个字符)。 对于每个 i,所有 s[j](0 <= j < i)插入到字典树中。字典树每个节点还要记录经过该点的字符串的数目。

如此一来我们考查 s[j] 和 k,如果第 i 位有 2i >= k,那么,如果选择与 s[j] 的第 i 位不相同的位,那么亦或的结果中就有 2i,也就是说一定满足要求,故上述方向上字典树里的单词数目应该全部加到答案上,字典树上只需走相反的方法再计算;反之,如果 2i < k,如果选择与 s[j] 的 第 i 相同的位,当前位的亦或为 0,后面即使全部都是 1 也不会比 2i 大,故肯定是不能让总亦或值大于等于 k,所以一定要走另外的方向,并且使得 k -= 2i

综上,字典树的每一层只走一个点。当然,计数完 s[j] 之后要将 s[j] 插入字典树,以便判断下一个数。总之,可以在 O(nlog(n)) 的时间内通过本题。

Code

  1. #include <stdio.h> 
  2. #include <string.h> 
  3.  
  4. #include <vector> 
  5.  
  6. const int N = 1000000 + 100
  7.  
  8. int a[N]; 
  9.  
  10. struct trie_t 

  11. #define each_bit(i) for (int i = 30; i >= 0; --i) 
  12.  
  13. struct node 

  14. int nxt[2]; 
  15. int cnt; 
  16.  
  17. node() 

  18. cnt = 0
  19. nxt[0] = nxt[1] = -1

  20. }; 
  21.  
  22. std::vector<node> v; 
  23.  
  24. void init() 

  25. // v.clear(); 
  26. v.push_back(node()); 

  27.  
  28. void insert(int x) 

  29. int now = 0
  30. v[0].cnt++; 
  31. each_bit(i) { 
  32. int b = ((x >> i) & 1); 
  33. if (v[now].nxt[b] == -1) { 
  34. v[now].nxt[b] = v.size(); 
  35. v.push_back(node()); 

  36. now = v[now].nxt[b]; 
  37. v[now].cnt++; 


  38.  
  39. int count(int x, int k) 

  40. int now = 0
  41. int ans = 0
  42. each_bit(i) { 
  43. int b = ((x >> i) & 1); 
  44. if ((1 << i) >= k) { 
  45. // numbers via nxt[!b] are big enough 
  46. if (v[now].nxt[!b] != -1) { 
  47. ans += v[v[now].nxt[!b]].cnt; 

  48. now = v[now].nxt[b]; 

  49. else { // (1 << i) < k 
  50. // numbers via nxt[b] are too small 
  51. now = v[now].nxt[!b]; 
  52. k -= (1 << i); 

  53. if (now == -1) { 
  54. break


  55. return ans; 

  56. #undef each_bit 
  57. } trie; 
  58.  
  59. int main() 

  60. int n, k; 
  61. scanf("%d %d", &n, &k); 
  62. for (int i = 1; i <= n; ++i) { 
  63. scanf("%d", a+i); 
  64. a[i] ^= a[i-1]; 

  65.  
  66. long long ans = 0
  67. trie.init(); 
  68. for (int i = 0; i <= n; ++i) { 
  69. ans += trie.count(a[i], k); 
  70. trie.insert(a[i]); 

  71. printf("%I64d\n", ans); 
  72. return 0

转载于:https://www.cnblogs.com/gu-castle/p/5518237.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值