1 条题解
-
0
灯泡开关问题 详细题解
问题核心转化
有编号为 的无限个灯泡,初始状态均为关闭。 第 轮:切换所有 的倍数的灯泡; 第 轮:切换所有 的倍数的灯泡; 第 轮:切换所有 的倍数的灯泡; 无限轮操作后,求第 个亮着的灯泡的编号。
一、数学推导:灯泡最终状态规律
1. 灯泡状态与约数个数的关系
灯泡 被切换的次数 = 的正约数的个数。
- 初始状态:关闭
- 切换偶数次:最终亮
- 切换奇数次:最终灭
因此:
- 若 有偶数个约数 灯泡亮
- 若 有奇数个约数 灯泡灭
2. 约数个数的奇偶性规律
正整数的约数都是成对出现的(如 的约数:),因此绝大多数数的约数个数为偶数。 只有完全平方数()存在一个约数是自身平方根(如 ),约数会重复,最终约数个数为奇数。
结论:
- 非完全平方数 灯泡亮
- 完全平方数 灯泡灭
问题简化为:求第 个非完全平方数。
二、核心公式推导
设目标数为 ,在 中:
- 完全平方数的个数为 (向下取整)
- 非完全平方数的个数为
我们需要找到最小的 ,满足:
1. 二分查找解法(标程思路)
二分查找的核心:在范围 内寻找满足 的最小 。
- 左边界 ,右边界 (覆盖极大数据范围)
- 取中间值 ,计算 内非完全平方数的个数
- 若个数 ,说明答案在左半区间,调整右边界;否则调整左边界
- 最终 即为第 个非完全平方数
2. 直接公式解法
数学推导可得直接计算公式:
该公式可直接计算出答案,效率更高。
三、标程代码解析
#include <bits/stdc++.h> using namespace std; int main(){ int t; cin >> t; // 输入测试用例数 while(t--){ // 循环处理每个测试用例 long long k, l = 1, r = 2e18; cin >> k; // 输入目标k // 二分查找核心逻辑 while(r - l > 1){ long long mid = (l + r) >> 1; // 等价于 (l+r)/2,避免溢出 // 计算1~mid中非完全平方数的个数,sqrtl为高精度平方根函数 long long cnt = mid - (long long)sqrtl(mid); if(cnt >= k) r = mid; // 个数足够,答案在左区间 else l = mid; // 个数不足,答案在右区间 } cout << r << "\n"; // 输出最终答案 } return 0; }关键细节说明
- 数据类型:使用
long long避免数据溢出( 可极大) - 高精度平方根:
sqrtl()用于长整型的平方根计算,保证精度 - 二分边界:右边界设为 ,覆盖所有题目可能的输入范围
- 循环条件:
r-l>1确保最终找到唯一解
四、示例验证
- 当 :第 个非完全平方数是 ,公式:
- 当 :第 个非完全平方数是 ,公式:
- 当 :第 个非完全平方数是 ,公式:
验证结果完全正确。
总结
- 灯泡亮灭等价于判断是否为非完全平方数;
- 问题转化为求第 个非完全平方数;
- 核心公式:;
- 解法:二分查找(标程)/ 直接公式 $\boldsymbol{n = \lfloor k + \sqrt{k} + 0.5 \rfloor}$。
- 1
信息
- ID
- 6334
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 2
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者