本文最后更新于 8 个月前,文中所描述的信息可能已发生改变。
E. Data Structures Fan 解题思路
核心问题分析
题目给定一个数组 和一个二进制字符串 。 我们需要处理两种操作:
- 区间反转: 给定区间 ,将字符串 在该区间内的所有字符反转()。
- 查询异或和: 给定 ,计算所有满足 的下标 对应的 的异或和。
数据范围 ,如果每次修改都遍历区间更新字符串,时间复杂度为 ,会超时。我们需要 或 的处理方式。
1. 异或运算的性质利用
异或运算(XOR, )有几个关键性质:
- (自反性)
- 交换律与结合律
维护全局变量: 我们可以维护两个全局变量:
- :当前 的所有 的异或和。
- :当前 的所有 的异或和。
对于查询操作(Type 2),直接输出对应的 或 即可,复杂度 。问题的关键在于如何快速处理修改操作(Type 1)。
2. 区间反转的快速更新
假设我们要反转区间 。 令区间 内所有元素的异或和为 。
推导过程: 在区间 内:
- 一部分 原本属于 (因为 )。反转后,它们将变成 ,应该从 中移除,并加入到 中。
- 另一部分 原本属于 (因为 )。反转后,它们将变成 ,应该从 中移除,并加入到 中。
利用异或的性质:从异或和中移除一个数等价于再异或一次该数(因为 )。
因此,对于区间 内的任意 :
- 无论它之前属于 还是 ,在反转后,它的归属都会对调。
- 我们只需要将 异或到 上,就能同时完成“移除原本属于 的部分”和“加入原本属于 的部分”。
- 同理,将 异或到 上,也能完成对应的更新。
结论: 当执行区间 反转时:
3. 前缀异或和数组
为了在 时间内求出 ,我们需要预处理前缀异或和数组 : 。
则:
4. C++ 代码实现细节
- 预处理:
- 计算 的前缀异或数组
data_vector。 - 根据初始字符串 ,计算初始的
data_all_zero() 和data_all_one()。
- 计算 的前缀异或数组
- 处理 Query 1 (反转):
- 计算区间异或和
range_xor = data_vector[end] ^ data_vector[start - 1]。 data_all_zero ^= range_xor。data_all_one ^= range_xor。
- 计算区间异或和
- 处理 Query 2 (询问):
- 根据输入直接输出
data_all_zero或data_all_one。
- 根据输入直接输出
cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve() {
int data_number, query_number;
string data_string;
cin >> data_number;
// data_temp 用于存储原始数组
// data_vector 用于存储前缀异或和
vector<int> data_temp(data_number + 1);
vector<int> data_vector(data_number + 1);
int data_all_zero; // 对应 s[i] == '0' 的异或和
int data_all_one; // 对应 s[i] == '1' 的异或和
for (int i = 1; i <= data_number; i++) {
cin >> data_vector[i];
}
data_temp = data_vector; // 备份原始数据用于初始计算
cin >> data_string;
// 1. 预处理前缀异或和
// data_vector[i] = a[1] ^ ... ^ a[i]
for (int i = 1; i <= data_number; i++) {
data_vector[i] = data_vector[i - 1] ^ data_vector[i];
}
// 2. 计算初始状态的 data_all_zero 和 data_all_one
// 技巧:data_all_zero 可以先设为所有数的异或和 (即 data_vector[n])
// 但下面的循环分别计算更直观
data_all_zero = 0;
data_all_one = 0;
// 注意:题目中数组是 1-based,字符串是 0-based,需要注意下标对齐
for (int i = 1; i <= data_number; i++) {
if (data_string[i - 1] == '1') {
data_all_one ^= data_temp[i];
} else {
data_all_zero ^= data_temp[i];
}
}
cin >> query_number;
for (int i = 1; i <= query_number; i++) {
int object;
cin >> object;
if (object == 1) {
// 区间反转操作
int start, end;
cin >> start >> end;
// 计算区间 [start, end] 的异或和
int range_val = data_vector[end] ^ data_vector[start - 1];
// 核心逻辑:区间内所有数的状态反转
// 意味着这部分异或和从 zero 集合移动到了 one 集合,反之亦然
// 利用异或性质,直接异到两个变量上即可同时完成 增加/删除
data_all_one ^= range_val;
data_all_zero ^= range_val;
} else if (object == 2) {
// 查询操作
int target;
cin >> target;
if (target == 1) {
cout << data_all_one << ' ';
} else if (target == 0) {
cout << data_all_zero << ' ';
}
}
}
cout << endl;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
cin >> t;
while (t--) {
solve();
}
}