小R有一个长度为 n的非负整数序列 a1,a2,...,an。定义一个区间 l,r的权值为 al,al+1,...,ar的二进制按位异或和,即 al⊕al+1⊕⋯⊕ar,其中⊕表示二进制按位异或。小X给了小R一个非负整数k。小X希望小R选择序列中尽可能多的不相交的区间,使得每个区间的权值均为k。两个区间 [l1,r1],[l2,r2]相交当且仅当两个区间同时包含至少一个相同的下标,即存在 1≤i≤n使得 l1≤i≤r1且 l2≤i≤r2。你需要帮助小R求出他能选出的区间数量的最大值。
需要用到的思想或方法:
- 异或运算性质
- 前缀异或
- 哈希表查询
- 贪心
异或运算
运算符^,运算过程即转化二进制,按位比较,异1同0。
前缀异或
为了方便,下面统一用1-based,不管是位置还是下标。
某位置i的前缀异或一般讲就是从开始到当前位置的连续异或
P[i] = a[1]⊕...⊕a[i]
这道题里要算区间异或,它和前缀异或的关系是
xor(l,r)=P[r]⊕P[l−1]
两边乘上P[r]
P[r]⊕xor(l,r)=P[l−1]
我们只要查询P[l-1]出现位置就能找到合法区间
哈希表
那么怎么查找呢,数组因为太大out了,所以要用到字典(红黑树),或者哈希表
使用时要包含头文件
#include <map> //字典,特点是按键排序
#include <unodered_map> //哈希表,无序作用简单来说就是提供自定义的映射关系(从key到value),可实现关系的高效储存和查询
用法(以哈希表为例):
unordered_map <int, int> dict; //定义
unordered_map <int, int> dict{{1,1}{2,2}};//带初始化
auto it = dict.find(need);//查找
int elem = dict.at(3);//另一种查找
if(it != dict.end())...//找到该元素
cout<<dict[1]; //调用与数组类似贪心
贪心是很简单的思想,体现在这道题里就是:
- 按右端点从小到大处理
- 对于每个右端点,选择左端点最靠右的合法区间(即最短区间)
- 保证新区间与已选区间不重叠
最后构建代码
#include <iostream>
#include <stdio.h>
#include <vector>
#include <unordered_map>
using namespace std;
int main() {
int n, k;
cin >> n >> k;
vector <int> a(n);
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
int count = 0, x = 0, need,position=0;
unordered_map <int, int> last_pos;
last_pos[0] = 0;
for (int i = 0; i < n; i++) {
x ^= a[i];
need = k ^ x;
auto it = last_pos.find(need);
if (it != last_pos.end() && it->second >= position) {
count++;
position = i;
}
last_pos[x] = i;
}
cout << count << endl;
return 0;
}