1 条题解
-
0
一、先看懂题目规则(简化版)
我们有一个区间 ,递归执行:
- 计算中点
- 区间长度为奇数:把 加入答案,再分裂成左右两个子区间
- 区间长度为偶数:直接分裂成两个子区间
- 长度 :停止递归
最终求所有被选中的中点之和。
二、标程背后的核心数学结论(最关键)
这道题的答案可以写成一个极其简洁的公式:
其中 是一个二进制特征值,由 在不断除以 2 的过程中是否为奇数决定。
标程的所有操作,都是在快速计算 。
三、为什么公式成立?(规律推导)
1. 选中的数字有什么规律?
观察递归过程:
- 每次处理一个奇数长度区间,我们取它的中点
- 所有被选中的中点 ,满足一个优美性质:
比如样例 :
- 选中 ,对称点 ,和为
- 选中 ,对称点 ,和为 总和
这里的 就是标程里的 。
2. 是什么?
= 有多少个数字被选中。
四、标程如何快速计算 ?(位运算魔法)
标程核心循环(逐行解释):
int mul = n + 1, sum = 0, cur = 1; while (n >= k) { // 只要长度 >=k 就继续分裂 if (n & 1) sum += cur;// 如果 n 是奇数,计数 +cur n >>= 1; // n = n / 2(向下取整) cur <<= 1; // cur = cur * 2 }逐行翻译
n & 1:判断 是否为奇数- 是奇数 → 这个区间会选中一个中点 →
sum += cur
- 是奇数 → 这个区间会选中一个中点 →
n >>= 1:等价于 (模拟区间分裂)cur <<= 1:等价于 (统计每一层的节点数量)
一句话总结: 这个循环在统计:从原长 不断折半,所有 且长度为奇数的层数,就是 。
五、完整计算流程(样例演示)
样例 1:
mul = 7+1 = 8 sum = 0, cur=1 n=7 >=2:7是奇数 → sum=1 n=3, cur=2 n=3 >=2:3是奇数 → sum=1+2=3 n=1, cur=4 → 退出循环 ans = 8 * 3 / 2 = 12和样例输出完全一致!
六、标程完整代码注释版
#include <bits/stdc++.h> #define int long long // 必须开 long long,数据极大 using namespace std; signed main() { ios::sync_with_stdio(false); cin.tie(0); int T; cin >> T; while (T--) { int n, k; cin >> n >> k; int mul = n + 1; // 对称和:x + 对称点 = n+1 int sum = 0; // S:被选中的中点数量 int cur = 1; // 每层的权重(1,2,4,8...) // 模拟区间不断折半 while (n >= k) { if (n & 1) { // 如果当前长度是奇数,会选中一个中点 sum += cur; } n >>= 1; // 长度除以 2 cur <<= 1; // 权重乘以 2 } // 核心公式 cout << mul * sum / 2 << '\n'; } return 0; }
七、算法复杂度(完美适配数据范围)
- 每次循环 除以 ,最多循环 次
- 每组用例 , 总操作数 ,完全不超时
- 所有变量用
long long,避免溢出
八、极简总结(背下来就能写)
- 答案公式
- 计算方法 把 不断除以 ,只要结果 且是奇数,就累加权重
- 标程本质 用位运算模拟递归分裂, 秒算答案。
- 1
信息
- ID
- 6500
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者