BISHI8 大整数哈希¶
简单通过率 38.13%python3样例通过牛客 AC
讲解章节:哈希与字符串哈希
一句话
维护 f: [0,2^64) -> [0,2^64),每次先取旧值再赋新值, 求 sum(i * ans_i) mod 2^64。
解题思路¶
这题考什么¶
名字叫「大整数哈希」,本质是「键的值域是 2^64、开不下数组」时怎么做映射。 C++ 里要手写哈希表(或 unordered_map + 自定义哈希防卡),Python 直接用 内置 dict 即可:dict 就是哈希表,且大整数的 hash 是 x mod (2^61-1), 出题人无法针对 Python 构造哈希攻击数据。 f(x) 初始为 0 这一点用 dict.get(x, 0) 天然表达,不需要预先填充。
数据规模与复杂度¶
n <= 5e6(本系列最大的一档,输入可达上百 MB),算法本身是 O(n) 的 哈希表查询 + 赋值,所以全部时间都花在读入和解释器循环上,写法必须抠:
- 不能 sys.stdin.buffer.read().split() 一把梭:那会一次性生成约 1e7 个 bytes 对象(每个至少 33 字节),光 token 列表就 400MB+,直接 MLE; 改成每次读 4MB 分块,块内 split 处理完就丢,内存峰值只和 dict 有关;
- 分块的边界可能把一个数字劈成两半,所以要把「块尾未闭合的 token」 留到下一块;同理一对 (x, y) 也可能跨块,用 carry 存住落单的那个;
- 取模只在最后做一次:中间和最多约 2^113,Python 大整数才 2 个 limb, 每步都 & MASK 反而更慢。
坑在哪¶
- 输出的是赋值前的旧值 ans_i,赋值要放在累加之后;
- i 从 1 开始计数,不是从 0;
- mod 2^64 是无符号截断,Python 里就是 & ((1<<64)-1), 因为过程中全是非负数,不必担心 C 里的有符号溢出 UB;
- 旧值为 0(x 第一次出现)时那一项贡献为 0,可以直接跳过累加;
- 最坏情况下 dict 会存 5e6 个大整数键值对,内存本身就很吃紧—— 这也是这题在 Python 下真正的难点(值域大到只能靠哈希表,没有别的招);
- 分块读入的两处拼接缺一不可:tail 处理「一个数字被块边界劈成两半」, carry 处理「x 在这一块、y 在下一块」。少了任何一个,都会在某个 4MB 边界上把数字读错,而且错得很隐蔽——小数据完全测不出来。
样例复核¶
n = 3、操作 (1,5)、(2,4)、(1,7): 第 1 次 f(1) 旧值 0,贡献 0,随后 f(1)=5; 第 2 次 f(2) 旧值 0,贡献 0,随后 f(2)=4; 第 3 次 f(1) 旧值 5,贡献 3*5=15,随后 f(1)=7。 合计 15,与样例一致。
前置章节¶
参考实现¶
[:octicons-arrow-left-16: BISHI7](BISHI7.md) [BISHI9 :octicons-arrow-right-16:](BISHI9.md)