杂谈 1:论 \([l, r]\) 之间的非平方数

Part 0 例题

先看一道例题;

给定两个数 \(l, r\),求 \(l \sim r\) 之间有多少个数不是平方数。平方数的定义:是一个数的平方。如 \(16\) 是平方数,因为 \(4^2 = 16\),\(11\) 则不是,因为 \(\sqrt{11}\) 不为整数。数据范围:\(1 \le l, r \le 10^9\)。

有些人一看到题目就像:“这不纯暴力吗?”。是的,大多数人看到题目的第一直觉是暴力。但是随着 \(l, r\) 的增大,暴力算法会跑得很慢,下面介绍几种算法来解决这个问题。

Part 1 纯暴力

直接枚举 \([l, r]\) 区间,对于每个 \(i\) 判断 \(\sqrt{i} - \lfloor \sqrt{i} \rfloor\) 是否为 \(0\)。(因为平方数开根是整数)如果是,计数器 \(+1\)。最后输出 \(r - l + 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';

但是,这种算法的时间复杂度是 \(O(r - l + 1)\)。如果 \(r - l + 1\) 超过 \(10^8\) 会超时(注意,sqrt 函数也占用一定时间)。下面就介绍另一种算法。

Part 2 小暴力

这种算法属于暴力,但不是纯暴力。大概的思路就是:定义两个变量 \(A, B\)。从 \(l\) 开始,一个一个数枚举,找到 \([l, r]\) 之间的最小的平方数并存进 \(A\) 里。再从 \(r\) 开始,一个一个数枚举,找到 \([l, r]\) 之间的最大的平方数并存进 \(B\) 里。

如果两个都没找到(即 \(A = B = 0\)),答案为 \(r - l + 1\)(因为 \(A = B = 0\) 表示 \([l, r]\) 之间没有平方数),否则答案为 \((r - l + 1) - (B - A + 1)\)(序列长度减去区间平方数,读者可自证为什么 \(B - A + 1\) 是 \([l, r]\) 之间的平方数个数)。

代码:

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';
}

设离 \(l\) 最近的比 \(l\) 大的平方数是 \(A\), 离 \(r\) 最近的比 \(r\) 小的平方数是 \(B\),这种算法的时间复杂度是 \(O(A - l + r - B)\)。如果 \(A\) 离 \(l\) 太远或 \(B\) 离 \(r\) 太远就会 TLE。

Part 3 二分

我们可以发现,平方数满足单调性(\(1, 4, 9, 16, 25, 36, \cdots\)),所以可以二分。

\(\sqrt{10^9} ≈ 31622\),我们可以构造一个 \(0 \sim 31622\) 的平方数数组(\(a_i = i^2\))。因此,我们可以二分在数组里找答案。找一个 \(L\) 使得 \(L^2 \ge l\),再找一个 \(R\) 使得 \(R^2 > r\),答案就是 \(r - l + 1 - \max(0,\ R - L)\)(跟上个算法的思想差不多,读者可自证)。

代码:

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';

这种算法的时间复杂度是 \(O(\log 10^9)\),还不够优秀。

Part 4 数学

令 \(A = \lfloor\sqrt{l}\rfloor \le \sqrt{l}\),\(B = \lfloor\sqrt{r}\rfloor \le \sqrt{r}\)。那么 \([l, r]\) 之间的平方数为 \((A + 1)^2, (A + 2)^2, \cdots, (B - 1)^2, B^2\)。如果 \(A^2 = l\),则答案为 \((r - l + 1) - (B - A + 1)\),否则答案为 \((r - l + 1) - (B - A)\)。(跟 Part \(2, 3\) 的思想差不多,读者可自证)

代码:

int A = sqrt(l), B = sqrt(r);
cout << (A * A == l? r - l + 1 - B - A + 1 : r - l + 1 - B - A) << '\n';

时间复杂度 \(O(1)\),极为优秀。

杂谈 1:论 [l, r] 之间的非平方数的更多相关文章

  1. UPC 2224 Boring Counting (离线线段树,统计区间[l,r]之间大小在[A,B]中的数的个数)

    题目链接:http://acm.upc.edu.cn/problem.php?id=2224 题意:给出n个数pi,和m个查询,每个查询给出l,r,a,b,让你求在区间l~r之间的pi的个数(A< ...

  2. HDU 6356 (线段树-l,r 之间小于val 的变val+单点求值)

    题目描述: 给你一个长度为n的最开始为0的数以及m个更新操作以及数据生成器参数X,Y,Z.每次操作,将由数据生成器生成出li,ri,vi.让你从区间[li,ri]中,将所有小于vi的数变为vi.最后让 ...

  3. 查询数组里有多少个数在[L,R]范围中(二分)

    使用两次二分即可得到这个值 比如现在有一个vector<int> vec,里面存放的是有序数列. 我们现在希望找出范围在[L,R]之间的数有多少个. 则有cnt = upper_bound ...

  4. hdu 4630 查询[L,R]区间内任意两个数的最大公约数

    No Pain No Game Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  5. 主席树——求区间[l,r]不同数字个数的模板(向左密集 D-query)

    主席树的另一种用途,,(还有一种是求区间第k大,区间<=k的个数) 事实上:每个版本的主席树维护了每个值最后出现的位置 这种主席树不是以权值线段树为基础,而是以普通的线段树为下标的 /* 无修改 ...

  6. 在一个由 'L' , 'R' 和 'X' 三个字符组成的字符串(例如"RXXLRXRXL")中进行移动操作。一次移动操作指用一个"LX"替换一个"XL",或者用一个"XR"替换一个"RX"。现给定起始字符串start和结束字符串end,请编写代码,当且仅当存在一系列移动操作使得start可以转换成end时, 返回True。

    在一个由 'L' , 'R' 和 'X' 三个字符组成的字符串(例如"RXXLRXRXL")中进行移动操作.一次移动操作指用一个"LX"替换一个"XL ...

  7. luogu P3834 【模板】可持久化线段树 1(主席树) 查询区间 [l, r] 内的第 k 小/大值

    ————————————————版权声明:本文为CSDN博主「ModestCoder_」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明.原文链接:https:// ...

  8. 中间值为什么为l+(r-l)/2,而不是(l+r)/2

    二分法的算法中,我们看到一些代码里取中间值: MID=l+(r-l)/2; 为什么是这个呢?不就是(l+r)/2吗?为什么要多此一举呢? 其实还是有不一样的,看看他们的区别吧: l,r是指针的时候只能 ...

  9. Uva 3767 Dynamic len(set(a[L:R])) 树套树

    Dynamic len(set(a[L:R])) Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 https://uva.onlinejudge.org/in ...

  10. 【题解】Luogu UVA12345 Dynamic len(set(a[L:R]))

    原题传送门 这题要用动态莫队,我博客里有介绍 这道题和luogu P1903 [国家集训队]数颜色 / 维护队列差不多,解法就在上面那篇博客里qaq 主要的问题是如何排序? 排序有三个关键字: 1.左 ...

随机推荐

  1. 开源免费真香!Star 1.4k 这款开源在线教育系统让万人学习零压力,企业培训系统一键搭建神器

    嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 PlayEdu 是一款基于 SpringBoot3 + Vue3 开发的开源企业培训系统,提 ...

  2. 【MOOC】华中科技大学操作系统慕课答案-期末测试题

    文章目录 单选题 填空题 判断题 主观题 单选题 1 ‎当操作系统处理缺页中断的时候,CPU处在 . A. 用户态 √B. 核态 C. 不确定的状态 D. 空闲状态 2 ‌操作系统的用户界面可以分为两 ...

  3. MFC单文档应用程序引入GDI+

    在MFC中引入GDI+,步骤如下: 1.在需要用到GDI+的文件中添加GDI+文件 1 #include <gdiplus.h> 2 using namespace Gdiplus; 2. ...

  4. java初级笔记(翁恺男神

    一.基础 1.类型转换 两个整数的运算结果只能是整数(自动向下取整 自动类型转换(由低变高 强制类型转换(从高到低 2.运算符优先级 正负号>乘除取余>加减连接>关系运算符>赋 ...

  5. Manim动画渲染:从代码到屏幕的幕后故事

    Manim是一个强大的动画制作库,它能够将简单的Python代码转化为精美的动画视频. 你是否好奇过,当你运行Manim代码时,背后的魔法是如何发生的呢? 今天,将重点介绍渲染过程中的三个关键步骤:S ...

  6. 8086汇编(16位汇编)学习笔记00.DEBUG命令使用解析及范例大全

    转载自:https://bpsend.net/thread-99-1-1.html 启动 Debug,它是可用于测试和调试 MS-DOS 可执行文件的程序. Debug [[drive:][path] ...

  7. Spring注解之@Autowired组件装配

    前言 说起Spring的@Autowired注解,想必大家已经耳熟能详:对于小编而言,虽然一直知道怎么用,但是并没有去了解过,因此,本文就梳理一下@Autowired注解的功能,如有写的不准确的地方, ...

  8. c#格式化字符串 toString()

    1.使用 ToString 方法的默认格式设置 public class Automobile { public string Name { get; set; } = "小王"; ...

  9. 如何通过Cgroups机制实现资源限制?

    1.什么是Cgroups? 在说Cgroups之前,我们先说说容器的"限制"问题. 我们知道通过Linux Namespace技术,可以实现容器中的进程看不到别的容器资源,但是有一 ...

  10. Go中的数组和切片

    本文参考: https://www.liwenzhou.com/posts/Go/05_array/ https://www.liwenzhou.com/posts/Go/06_slice/ Arra ...