杂谈 1:论 [l, r] 之间的非平方数
杂谈 1:论 之间的非平方数
Part 0 例题
先看一道例题;
给定两个数 ,求 之间有多少个数不是平方数。平方数的定义:是一个数的平方。如 是平方数,因为 , 则不是,因为 不为整数。数据范围:。
有些人一看到题目就像:“这不纯暴力吗?”。是的,大多数人看到题目的第一直觉是暴力。但是随着 的增大,暴力算法会跑得很慢,下面介绍几种算法来解决这个问题。
Part 1 纯暴力
直接枚举 区间,对于每个 判断 是否为 。(因为平方数开根是整数)如果是,计数器 。最后输出 减去计数器里的数即可。
代码;
ll ans = 0;
for (int i = l; i <= r; ++ i) {
if (sqrt(i) - int(sqrt(i)) == 0) {
++ ans;
}
}
cout << (r - l + 1) - ans << '\n';
但是,这种算法的时间复杂度是 。如果 超过 会超时(注意,sqrt
函数也占用一定时间)。下面就介绍另一种算法。
Part 2 小暴力
这种算法属于暴力,但不是纯暴力。大概的思路就是:定义两个变量 。从 开始,一个一个数枚举,找到 之间的最小的平方数并存进 里。再从 开始,一个一个数枚举,找到 之间的最大的平方数并存进 里。
如果两个都没找到(即 ),答案为 (因为 表示 之间没有平方数),否则答案为 (序列长度减去区间平方数,读者可自证为什么 是 之间的平方数个数)。
代码:
for (ll i = l; ; ++ i) {
if (sqrt(i) - int(sqrt(i)) == 0) {
A = sqrt(i);
break;
}
}
for (ll i = r; ; -- i) {
if (sqrt(i) - int(sqrt(i)) == 0) {
B = sqrt(i);
break;
}
}
if (!A && !B) {
cout << r - l + 1 << '\n';
} else {
cout << r - l + 1 - (B - A + 1) << '\n';
}
设离 最近的比 大的平方数是 , 离 最近的比 小的平方数是 ,这种算法的时间复杂度是 。如果 离 太远或 离 太远就会 TLE。
Part 3 二分
我们可以发现,平方数满足单调性(),所以可以二分。
,我们可以构造一个 的平方数数组()。因此,我们可以二分在数组里找答案。找一个 使得 ,再找一个 使得 ,答案就是 (跟上个算法的思想差不多,读者可自证)。
代码:
const int kMaxN = sqrt(1e9);
for (int i = 1; i <= kMaxN; ++ i) {
a[i] = i * i;
}
int L = 0, R = sqrt(1e9);
L = lower_bound(a, a + kMaxN, sqrt(l)) - a;
R = upper_bound(a, a + kMaxN, sqrt(r)) - a;
cout << r - l + 1 - max(0, R - L) << '\n';
这种算法的时间复杂度是 ,还不够优秀。
Part 4 数学
令 ,。那么 之间的平方数为 。如果 ,则答案为 ,否则答案为 。(跟 Part 的思想差不多,读者可自证)
代码:
int A = sqrt(l), B = sqrt(r);
cout << (A * A == l? r - l + 1 - B - A + 1 : r - l + 1 - B - A) << '\n';
时间复杂度 ,极为优秀。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效