杂谈 1:论 [l, r] 之间的非平方数
杂谈 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] 之间的非平方数的更多相关文章
- 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< ...
- HDU 6356 (线段树-l,r 之间小于val 的变val+单点求值)
题目描述: 给你一个长度为n的最开始为0的数以及m个更新操作以及数据生成器参数X,Y,Z.每次操作,将由数据生成器生成出li,ri,vi.让你从区间[li,ri]中,将所有小于vi的数变为vi.最后让 ...
- 查询数组里有多少个数在[L,R]范围中(二分)
使用两次二分即可得到这个值 比如现在有一个vector<int> vec,里面存放的是有序数列. 我们现在希望找出范围在[L,R]之间的数有多少个. 则有cnt = upper_bound ...
- hdu 4630 查询[L,R]区间内任意两个数的最大公约数
No Pain No Game Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...
- 主席树——求区间[l,r]不同数字个数的模板(向左密集 D-query)
主席树的另一种用途,,(还有一种是求区间第k大,区间<=k的个数) 事实上:每个版本的主席树维护了每个值最后出现的位置 这种主席树不是以权值线段树为基础,而是以普通的线段树为下标的 /* 无修改 ...
- 在一个由 'L' , 'R' 和 'X' 三个字符组成的字符串(例如"RXXLRXRXL")中进行移动操作。一次移动操作指用一个"LX"替换一个"XL",或者用一个"XR"替换一个"RX"。现给定起始字符串start和结束字符串end,请编写代码,当且仅当存在一系列移动操作使得start可以转换成end时, 返回True。
在一个由 'L' , 'R' 和 'X' 三个字符组成的字符串(例如"RXXLRXRXL")中进行移动操作.一次移动操作指用一个"LX"替换一个"XL ...
- luogu P3834 【模板】可持久化线段树 1(主席树) 查询区间 [l, r] 内的第 k 小/大值
————————————————版权声明:本文为CSDN博主「ModestCoder_」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明.原文链接:https:// ...
- 中间值为什么为l+(r-l)/2,而不是(l+r)/2
二分法的算法中,我们看到一些代码里取中间值: MID=l+(r-l)/2; 为什么是这个呢?不就是(l+r)/2吗?为什么要多此一举呢? 其实还是有不一样的,看看他们的区别吧: l,r是指针的时候只能 ...
- 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 ...
- 【题解】Luogu UVA12345 Dynamic len(set(a[L:R]))
原题传送门 这题要用动态莫队,我博客里有介绍 这道题和luogu P1903 [国家集训队]数颜色 / 维护队列差不多,解法就在上面那篇博客里qaq 主要的问题是如何排序? 排序有三个关键字: 1.左 ...
随机推荐
- pytorch 实战教程之路径聚合网络PANet(Path AggregationNetwork)代码实现 PANet原理详解
原文作者:aircraft 原文链接:pytorch 实战教程之路径聚合网络PANet(Path AggregationNetwork)代码实现 PANet原理详解 学习YOLOv5前的 ...
- 题解:CF1955E Long Inversions
简单题. 考虑贪心地进行修改,每次选择字符串中最左侧第一个 000,并以该位置为左端点进行一次修改,可以发现若 lenlenlen 合法则这样一定构造出全 111 串. 然而直接暴力实现是 O(n2) ...
- C#基础——不可变集合
参照:https://mp.weixin.qq.com/s/NFBc2bNeHpdadh89T44Lpg 在 .NET Core 中就存在着这三大 IReadOnlyList,IReadOnlyDic ...
- 聊聊@Autowired与@Resource的区别
1. 前言 从事过很多家公司,见过很多项目,发现@Autowired和@Resource的使用都是一样的乱, 一个项目中有使用@Autowired的,有使用@Resource的, 甚至有的类中一会儿使 ...
- SpringBoot——使用http2
使用http2 许多浏览器,包括Edge,仅在TLS(即HTTPS)情况下支持HTTP/2.即使服务器端配置为无TLS支持的HTTP/2,浏览器可能仍将回退到HTTP/1.1.所以我们需要有一个证书来 ...
- 代码随想录第十八天 | Leecode 530. 二叉搜索树的最小绝对差、501. 二叉搜索树中的众数、236. 二叉树的最近公共祖先
530. 二叉搜索树的最小绝对差 题目描述 给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 . 差值是一个正数,其数值等于两值之差的绝对值. 示例 1: 输入:roo ...
- Go-Spring v1.2.0 版本简介
引言 随着微服务和云原生架构的普及,Go 语言以其高并发.低延迟和简洁语法在后端开发领域迅速崛起.然而,原生 Go 在项目结构.依赖管理.配置热更新等方面相比 Java Spring 生态尚有短板.G ...
- TVM: VisitExpr流程分析
TVM源码中涉及到表达式遍历的地方,一般是适用VisitExpr接口进行,这个接口设计TVM的visitor模式,具体分析可参考:TVM:visitor设计模式 基类tvm::relay::ExprF ...
- Django 数据迁移报错之“TypeError: __init__() missing 1 required positional argument: 'on_delete'”
当执行 python manage.py makemigrations 出现错误:TypeError: init() missing 1 required positional argument: ' ...
- .NET外挂系列:3. 了解 harmony 中灵活的纯手工注入方式
一:背景 1. 讲故事 上一篇我们讲到了 注解特性,harmony 在内部提供了 20个 HarmonyPatch 重载方法尽可能的让大家满足业务开发,那时候我也说了,特性虽然简单粗暴,但只能解决 9 ...