首页 > 其他分享 >杂谈 1:论 [l, r] 之间的非平方数

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

时间:2024-01-31 12:01:11浏览次数:23  
标签:平方 暴力 int 杂谈 sqrt 算法 Part 之间

杂谈 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\),答案就是 \(\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 << 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)\),极为优秀。

标签:平方,暴力,int,杂谈,sqrt,算法,Part,之间
From: https://www.cnblogs.com/bc2qwq/p/17998985/sectionsquarenumber

相关文章

  • (一)浅谈ADO.NET对象之间的关系
    一、Database:数据源,这里比喻水源二、Connection:连接数据源对象,类似伸入到水源的水管三、Command:数据库语句执行对象,类似抽水机四、DataAdapter、DataReader:数据适配器,类似输水管五、DataSet:数据集,类似水库六、DataTable:数据表,类似水池......
  • List<?extends T>和List<? super T>之间有什么区别?
    表示类型的上界,也就是说参数化的类型可能是T或者T的子类。例如:下面的写法都是合法的赋值语句:![](https://img2024.cnblogs.com/blog/3383899/202401/3383899-20240129203144762-685357479.png)(1)在上面的赋值显示中,对读数据进行分析1)不管给List如何赋值,可以保证List里存放的一......
  • 2.WPF中控件类之间的继承关系
    在WPF中所有的控件都是继承DispatcherObject类,可以说在wpf中DispatcherObject是所有控件类的基类,而DispatcherObject却继承Object,而它所在的程序集是在WindowsBase.dll里。看一张图,wpf控件继承关系图 1.Shape类形状控件是WPF一大系列控件。WPF所有的形状控件都继承于Shape基......
  • Objective-C杂谈【1】
    ObjC(Objective-C)进入人们的视野,主要源自MacOSX的Cocoa。人们即使是开发着更多关注的也是Cocoa靓丽的外表,对支撑起Cocoa的ObjC确一直缺乏深入了解。ObjC给人深刻印象的无异于它与传统基于“.”的面向对象语言语法的完全不同的调用或者消息传递语法。例:[objectdoSomethingWithPa......
  • Go - 基本数据类型和其字符串表示之间转换
    1.基本数据类型和其字符串表示之间转换基本类型的值,都有一个字符串表示,如数字类型值1字符串表示为"1",字符的编码为Unicode或者UTF-8,数字的编码是int,底层存储的数据格式本质上不一样,基本类型的转换本质上只是文法语义上的转化1.1Go语言基本类型整数:有符号intint8i......
  • spring IOC DI 容器杂谈
    IOC容器的原理spring 博客收藏参考博客https://mp.weixin.qq.com/s?__biz=MzI4Njg5MDA5NA==&mid=2247484247&idx=1&sn=e228e29e344559e469ac3ecfa9715217&chksm=ebd74256dca0cb40059f3f627fc9450f916c1e1b39ba741842d91774f5bb7f518063e5acf5a0#rdhttps://www.zhihu.c......
  • vue3 解决导航栏和header之间的空白
    vue3解决导航栏和header之间的空白现象如下图所示,导航栏和header之间有白色空隙 解决components\CommonAside.vue修改样式,添加如下代码</style>.el-menu{height:100vh;border-right:none;h3{color:#fff;text-align:center;......
  • (2/60)有序数组平方、长度最小子数组、螺旋矩阵
    有序数组的平方leetcode:977.有序数组的平方给你一个按非递减顺序排序的整数数组nums,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。暴力法思路遍历数组,元素原地替换为自身平方值。将数组进行排序。复杂度分析时间复杂度:O(N+logN)空间复杂度:O(1)注......
  • 计算一个数的算术平方根
    从键盘输入一个小于1000的数,输出它的算术平方根,若算数平方根不为整数,则向下取整。#include<stdio.h>#include<math.h>intmain(){ inta=0; intb=0; while(scanf("%d",&a)) { if(a>0&&a<1000) { break; } else { printf(&qu......
  • 代码随想录算法训练营第二天|977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II
    977.有序数组的平方给你一个按非递减顺序排序的整数数组nums,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。题目链接:https://leetcode.cn/problems/squares-of-a-sorted-array/错误的vector遍历方式,这会导致访问越界!!!while(nums[flag]<0)flag++;倒也不难,我......