1 条题解
-
0
D1. 无限序列(简单版本)详细题解
一、题目理解
定义一个无限二进制序列 :
- 前 项给定:
- 对于 :$$a_m = a_1 \oplus a_2 \oplus \dots \oplus a_{\lfloor m/2 \rfloor} $$其中 是按位异或(对二进制来说,等同于模 加法)。
给定 (简单版本),需要输出 。
二、核心观察
1. 定义前缀异或
设
则:
- 对于 :
2. 递推式
将 代入:
$$S(m) = S(m-1) \oplus S(\lfloor m/2 \rfloor) \quad (m > n) $$3. 边界条件
- 可直接计算
三、标程算法解析
标程使用了一个巧妙的数学化简,避免了复杂的递归。
步骤 1:预处理前 项
vector<int> a(n + 1); vector<int> pref(n + 1); for (int i = 1; i <= n; i++) { pref[i] = pref[i - 1] + a[i]; }pref[i]是普通前缀和(不是异或),但这里实际上只需要奇偶性(模 )。步骤 2:将 调整为奇数
if (n % 2 == 0) { n++; int cur = pref[n / 2] & 1; a.push_back(cur); pref.push_back(pref.back() + cur); }为什么? 因为递推中 是奇数时规律更简洁。
步骤 3:再扩展 项
for (int i = n + 1; i <= n * 2; i++) { a.push_back(pref[i / 2] & 1); pref.push_back(pref[i - 1] + a[i]); }这样我们有了 。
步骤 4:计算
int p = pref[n] & 1;是 的奇偶性(即异或和)。
步骤 5:核心函数
get(x)auto get = [&](int64_t x) { int ret = 0; while (true) { if (x <= n * 2) { ret ^= a[x]; break; } ret ^= p; if ((x / 2 - n) % 2 == 0) { break; } x /= 2; } return ret; };这个函数计算 的逻辑:
- 如果 ,直接查表
- 否则,先异或上 (因为 的奇偶性等于 的若干倍)
- 然后检查条件,决定是否继续递归
四、数学原理
关键结论(通过打表/推导)
对于 ,有:
经过分析, 的奇偶性满足周期性和线性递推。标程通过扩展 项并利用 ,将任意 的 计算归结为:
- 不断除以 ,直到
- 每层贡献一个 (当条件满足时)
- 最终查表得到结果
五、为什么这样正确?
设 (即前缀和的奇偶性)。
从递推:
对于 ,可以证明:
- 的值只取决于 的二进制表示和
- 当 足够大时, 呈现规律性
标程中的循环就是在模拟这个“除以 ”的过程,每层检查奇偶性变化。
六、示例验证
以第一个测试用例为例:
n=1, a=[1], l=1- 是奇数,不扩展
- 计算
get(1):,返回
输出
1✅
七、复杂度分析
- 预处理:
- 每次
get(x):,因为 每次除以 - 总复杂度:,满足 ,
八、总结
要点 说明 核心思想 利用前缀异或的递推,将问题转化为奇偶性判断 数学化简 通过扩展 项和模 运算,避免递归 算法步骤 1. 补全 为奇数
2. 扩展至 项
3. 用循环除以 计算答案时间复杂度 空间复杂度
- 1
信息
- ID
- 7230
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者