1 条题解
-
0
Boneca Ambalabu 的异或问题 详细题解
问题描述
给定一个长度为 的整数序列 ()。对于每一个 (),定义:
$$S(k) = (a_k \oplus a_1) + (a_k \oplus a_2) + \cdots + (a_k \oplus a_n) $$要求求出 。其中 表示按位异或运算。
输入格式
第一行一个整数 ,表示测试用例数()。
每个测试用例第一行一个整数 ()。
第二行 个整数 ()。
保证所有测试用例的 之和不超过 。输出格式
对于每个测试用例,输出一行一个整数,表示最大可能的 。
核心思路:按位独立计算
计算 时,如果对每个 都直接计算,复杂度将达到 ,不可接受。
观察异或运算的性质:- 异或是按位独立的,即两个数异或结果的第 位只由这两个数在第 位上的值决定。
- 加法可交换、可结合,所以总和可以按位拆开:先求每一位上异或结果为 1 的次数,再乘以该位的权值 ,最后相加。
若我们固定一个 ,对于第 位(), 在该位为 的个数取决于:
- 如果 的第 位是 0:那么 的第 位是 当且仅当 的第 位是 。
我们设整个数组中第 位为 的元素个数为 ,则这种情况下的贡献为 次。 - 如果 的第 位是 1:那么 的第 位是 当且仅当 的第 位是 。
这样的 的个数是 。
因此,该位对 的总贡献可以写成:
- 若 的第 位为 0:贡献
- 若 的第 位为 1:贡献
这样,对于任何一个 ,我们只要知道 数组和 的值,就可以在 时间内算出 。
算法步骤
- 读入数据: 和数组 (代码中从下标 1 开始)。
- 预处理 数组:
- 初始化长度为 的全零数组 。
- 遍历每个元素 ,对每个位 (),若 ,则 加 1。
- 时间复杂度 。
- 计算每个 的 并更新答案:
- 初始化答案 。
- 对于每个 从 到 :
- 初始化 。
- 对于每个位 从 到 :
- 提取 的第 位 。
- 若 :
- 若 :
- 时间复杂度 。
- 输出 。
总时间复杂度 ,空间复杂度 。
标准程序详解
下面是题目提供的标准程序,我们将逐段分析。
#include <bits/stdc++.h> using namespace std; #define int long long // 避免溢出,使用 64 位整数 void solve() { int n; cin >> n; int arr[n+1]; // 存储原数组,下标从 1 开始 vector<int> cnt(30, 0); // 记录每一位 1 的个数 // 预处理 cnt for (int i = 1; i <= n; i++) { cin >> arr[i]; for (int j = 0; j < 30; j++) { cnt[j] += ((arr[i] >> j) & 1); } } int ans = 0; // 枚举每个可能的 k for (int i = 1; i <= n; i++) { int tot = 0; for (int j = 0; j < 30; j++) { bool f = ((arr[i] >> j) & 1); if (f) { tot += (1 << j) * (n - cnt[j]); // 第 j 位为 1,计算贡献 } else { tot += (1 << j) * cnt[j]; // 第 j 位为 0,计算贡献 } } ans = max(ans, tot); } cout << ans << "\n"; } signed main() { ios_base::sync_with_stdio(0); cin.tie(0); int t; cin >> t; while (t--) solve(); return 0; }
- 1
信息
- ID
- 6742
- 时间
- 2000ms
- 内存
- 256MiB
- 难度
- 4
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者