1 条题解
-
0
题目理解
我们有 种馅料排成一排,第 种的属性值为 。
一个粽子可以由区间 的馅料混合而成,美味度是这些 的异或和。我们要选 个不同的区间,使得它们的异或和的总和最大。
1. 问题转化
设 为前缀异或和:
规定 。
那么区间 的异或和可以表示为:
因此,问题变成:
在所有满足 的 对中,选出 对,使得它们的 的和最大。注意这里 对应 , 对应 ,所以 是必须的,且 从 到 , 从 到 。
2. 最大化和问题
我们要求 个不同的 对的异或值的和的最大值。
这是一个经典问题:从 个数 中选出 对不同的 (),使它们的异或值之和最大。
3. 思路分析
如果 很小,我们可以用堆(类似”第 k 大异或对“的方法)来解。
方法:
- 把所有的 建成一棵 01-Trie。
- 对于每个 ,我们可以在 Trie 上找到与 异或值最大的 ()。
- 用一个最大堆维护可能的候选对 ,其中 是 与 Trie 中某个值的异或值, 表示这是对于 的第几大异或值(从大到小排序)。
- 每次从堆顶取出最大的异或值,累加到答案,然后把这个 对应的下一个小的候选异或值插入堆。
4. 具体步骤
4.1 Trie 结构
Trie 节点存储:
- 两个子节点指针(0 和 1)
- 经过该节点的数的数量(用于找第 大异或值)
4.2 查询与 异或第 大的值
我们从高位到低位走,尽量选择与 当前位不同的分支(为了最大化异或值),如果该分支的数量小于 ,则走另一分支并调整 。
这里我们实际上需要的是:对于给定的 ,找到与 异或第 大的值( 是最大, 是次大,等等),并且保证 且 在 Trie 中。
5. 算法流程
- 构建前缀异或数组 。
- 将 全部插入 Trie。
- 对每个 ,计算与 异或最大的值及其对应的 (要保证 ,但这里 Trie 中有重复值时需要特殊处理,不过题目 不同并不能保证 不同,所以可能有重复 值,但 不同就算不同区间,所以允许 重复)。
- 初始化最大堆,堆中元素为 ,其中 表示当前取的是第 1 大的异或值。
- 重复 次:
- 弹出堆顶 ,累加 到答案。
- 对于这个 ,找它第 大的异或值,如果存在,则 入堆。
6. 时间复杂度
- 建 Trie:, 是值域,最大 。
- 每次查询第 大异或值:。
- 堆操作:。
总复杂度 ,可以接受。
7. 例子验证
样例:
n=3, k=2 a = [1, 2, 3]前缀异或:
数:。
所有 对的异或值():
- (0,1): 1
- (0,2): 3
- (0,3): 0
- (1,2): 2
- (1,3): 1
- (2,3): 3
最大的两个是 和 ,和为 ,符合样例。
8. 公式总结
设:
- 前缀异或 ,
- 区间 异或值
- 我们要选 个不同的 使得 最大
等价于:
$$\max_{\substack{P \subset \{(i,j) \mid 0 \le i < j \le n\} \\ |P| = k}} \sum_{(i,j) \in P} (S_j \oplus S_i) $$
9. 实现细节(伪代码)
1. S[0] = 0 2. for i = 1 to n: S[i] = S[i-1] xor a[i] 3. Build Trie with all S[0..n] 4. max_heap = [] 5. for i = 0 to n: value, idx = query_max_xor(S[i]) # 保证 idx != i 如果需要,可以查两次 push (value, i, 1) to max_heap 6. ans = 0 7. for _ in range(k): pop (v, i, rk) from heap ans += v next_val = query_kth_xor(S[i], rk+1) # 第 rk+1 大的异或值 if next_val exists: push (next_val, i, rk+1) to heap 8. output ans
这样我们就得到了一个高效的 解法,可以处理 的大数据。
- 1
信息
- ID
- 4521
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者