正方形计数 题解

news/2024/11/15 19:58:27/文章来源:https://www.cnblogs.com/XuYueming/p/18422474

题意简述

给出一个 \(n \times n\) 的格点平面,有 \(q\) 次询问,求有多少正方形以 \((x, y)\) 为某一顶点,满足这个正方形顶点均在格点上,且边长为有理数。

\(l \leq 10^5\)\(q \leq 5 \times 10^5\)

题目分析

看到边长为有理数,想到「毕达哥拉斯三元组」("Pythagorean triple",简称「三元组」)。在 \(n\) 范围内,这样的三元组是不多的,根据下文的生成方法可以证明组数是 \(\Theta(n \log n)\) 的。我们先来讨论如何生成出这些「三元组」。

本文中只介绍最简单的「欧几里得公式」("Euclid's formula")来生成所有 \(n\) 范围内的「三元组」。如果读者有兴趣,可以尝试阅读 Formulas for generating Pythagorean triples,并使用其他方法来解决此问题。

设生成的「三元组」为 \((a, b, c)\),那么我们根据参数 \(\varphi, \lambda, k\),其中 $\varphi \gt \lambda \gt 0, \lambda \perp \varphi , k \in\mathbb{Z}_+ $ 且 \(\lambda\) 和 $\varphi $ 中恰有一个偶数,根据下述公式即可生成所有「三元组」:

\({\displaystyle a=k\cdot (\varphi ^{2}-\lambda^{2}),\ \,b=k\cdot (2\varphi \lambda),\ \,c=k\cdot (\varphi ^{2}+\lambda^{2})}\) [1]

\(k = 1\) 时生成出来的 \((a, b, c)\) 为「本原三元组」("primitive triple"),如 \({\displaystyle \varphi =2,\ \,\lambda=1,\, k = 1}\) 生成出 \((3, 4, 5)\)。所有「三元组」都能唯一地被某一「本原三元组」和缩放系数 \(k\) 生成。

使用初等代数展开,就能证明生成的算法的充分性,这部分不在赘述。考虑证明其必要性,即所有「本原三元组」都能用这样的方式表示出来。

证明 [1:1]

「本原三元组」\((a, b, c)\) 满足 \(a^2 + b^2 = c^2\),且 \(\gcd(a, b, c) = 1\)。那么有 \(a\)\(b\)\(c\) 两两互质,那么 \(a\)\(b\) 间至少一个是奇数,不妨令 \(a\) 为奇数,那么 \(b\) 为偶数且 \(c\) 为奇数(如果 \(a\)\(b\) 都是奇数,那么 \(c^2\) 会是偶数,那么 \(c\) 也会是偶数,那么 \(4 \mid c^2\),而 \(a^2 + b^2 \bmod 4 = 2\),矛盾)。

我们得到 \(c^2 - a^2 = b^2\),因此 \((c-a)(c+a) = b^2\),即 \(\dfrac{c+a}{b} = \dfrac{b}{c-a}\)\(\dfrac{c+a}{b}\) 显然为有理数,设其最简形式为 \(\dfrac{\varphi }{\lambda}\)。因此 \(\dfrac{c-a}{b} = \dfrac{\lambda}{\varphi }\)。我们有:

\[\frac{c}{b} + \frac{a}{b} = \frac{\varphi }{\lambda}, \quad \quad \frac{c}{b} - \frac{a}{b} = \frac{\lambda}{\varphi } \]

解出 \(\dfrac{c}{b}\)\(\dfrac{a}{b}\) 分别为:

\[\frac{c}{b} = \frac{1}{2}\left(\frac{\varphi }{\lambda} + \frac{\lambda}{\varphi }\right) = \frac{\varphi ^2 + \lambda^2}{2\varphi \lambda}, \quad \quad \frac{a}{b} = \frac{1}{2}\left(\frac{\varphi }{\lambda} - \frac{\lambda}{\varphi }\right) = \frac{\varphi ^2 - \lambda^2}{2\varphi \lambda}. \]

由于 \(\varphi \perp \lambda\),故它们不能同为偶数。若它们同为奇数,则 \(\dfrac{\varphi ^2 - \lambda^2}{2\varphi \lambda}\) 的分子将是 \(4\) 的倍数,而分母关于 \(2\) 的因子有且仅有一个 \(2\),导出 \(a\) 必为偶数,和我们假设 \(a\) 为奇数矛盾。因此,\(\lambda\) 和 $\varphi $ 中恰有一个偶数,所以 \(\varphi ^2 \pm \lambda^2\) 为奇数。因此,这些分数是最简分数(如果有一个奇质数 \(p\) 为分子分母的公因数,由于 \(\lambda\) 和 $\varphi $ 的奇偶性,只能整除 \(\lambda\) 或 $\varphi $ 其中一个,那么就不可以整除分子 \(\varphi ^2 \pm \lambda^2\),矛盾)。由此将分子与分子相等,分母与分母相等,得到欧几里得公式:

\[a = \varphi ^2 - \lambda^2, \quad b = 2\varphi \lambda, \quad c = \varphi ^2 + \lambda^2 \]

我们可以 \(\Theta(\sqrt{n})\) 地枚举 \(\varphi\),再 \(\mathcal{O}(\sqrt{n})\) 的枚举 \(\lambda \lt \varphi\),这也说明了 \(a, b, c \leq n\) 的「本原三元组」是 \(\Theta(\sqrt{n})\cdot\mathcal{O}(\sqrt{n}) = \mathcal{O}(n)\) 的。事实上,这是一个较松的上界。

根据 \(\varphi ^2 + \lambda^2 \leq c = k \cdot(\varphi ^2 + \lambda^2) \leq n\),得到 \(\lambda \leq \sqrt{n - \varphi ^2}\)。同时结合 \(\lambda \lt \varphi\)。得到枚举 \(\lambda\) 更精确的时间复杂度是 \(\Theta \left(\min \left\{ \sqrt{n - \varphi ^2}, \varphi - 1 \right\}\right)\)

由于 \(\min \left\{ \sqrt{n - \varphi ^2}, \varphi-1 \right\} \leq \varphi-1\),我们不妨按照 \(\varphi - 1\) 计算上界。

此时内层为 \({\displaystyle \mathcal{O}\left(\sum_{\lambda=1}^{\varphi - 1} \dfrac{n}{\varphi ^2 + \lambda^2}\right) \leq \mathcal{O}\left(\sum_{\lambda=1}^{\varphi - 1} \dfrac{n}{\varphi ^2}\right) = \mathcal{O}\left(\dfrac{n}{\varphi}\right)}\)。其中第二步放缩是在分母中,直接去掉了 \(\lambda^2\) 这一项,所以是正确的。如果由于 \(\lambda \lt \varphi\),把分母看做 \(2\lambda^2\),这样最后得到的上界是很松的 \(\Theta(n\sqrt{n})\),我在这里一开始理解错了,疑惑了好久。

我们开始化简总的时间复杂度的式子。

\[\begin{aligned} &\quad\ \mathcal{O}\left(\sum_{\varphi=1}^{\sqrt{n}} \sum_{\lambda=1}^{\min \left\{ \sqrt{n - \varphi ^2}, \varphi - 1 \right\}} \dfrac{n}{\varphi ^2 + \lambda^2} \right) \\ & \leq \mathcal{O}\left(\sum_{\varphi=1}^{\sqrt{n}} \sum_{\lambda=1}^{\varphi-1} \dfrac{n}{\varphi ^2 + \lambda^2} \right) \\ & \leq \mathcal{O}\left(\sum_{\varphi=1}^{\sqrt{n}} \frac{n}{\varphi} \right) \\ & \leq \mathcal{O}\left(n \log n \right) \\ \end{aligned} \]

话说回来,我们使用 \(\Theta(n \log n)\) 的算法找出了 \(n\) 以内所有「三元组」,有什么用呢?我们发现一个斜着的正方形,可以通过四条边分别往外做四个全等的直角三角形,且三角形三边为我们求出的「三元组」,变成一个正的正方形。这就是「改斜归正」。为了方便讨论,我们可以特殊地令「三元组」\((a, b, c)\) 其中可以有 \(0\),也就是本来就是正的正方形,也可以这样操作。

改斜归正

注意到,对于 \(a, b \neq 0\),交换 \(a, b\),也就是将上图左右翻转,也是合法的。我们现在考虑 \((a, b, c)\) 对整个平面哪些点有贡献。我们不妨钦定询问的点,作为这些正方形的某一顶点,然后考虑边界情况,可以得到下图。

对某一矩形的贡献

发现,如果询问的点在红色矩形内,就可以用这组 \((a, b, c)\) 做出以 \((x, y)\) 为左顶点的合法正方形。可以离线下来做扫描线。其它 \(3\) 个端点类似处理即可。总共 \(4\) 次扫描线。或者由于图形很强的对称性,可以尝试优化常数。

时间复杂度 \(\Theta(n \log^2n + q \log n)\)

代码

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;const int N = 100010, Q = 500010;int n, q, ans[Q];
vector<pair<int, int>> line[N], qx[N], qy[N], triple;struct Bit_Tree {static constexpr inline int lowbit(int x) {return x & -x;}int tree[N];inline void clear() {memset(tree + 1, 0x00, sizeof (int) * (n));}inline void modify(int p, int v) {for (int i = p; i <= n; i += lowbit(i))tree[i] += v;}inline void modify(int l, int r, int v) {modify(l, v), modify(r + 1, -v);}inline int query(int p) {int res = 0;for (int i = p; i; i -= lowbit(i))res += tree[i];return res;}
} yzh;signed main() {#ifndef XuYuemingfreopen("WSDR.in", "r", stdin);freopen("WSDR.out", "w", stdout);#endifscanf("%*d%d%d", &n, &q);for (int i = 1; i * i <= n; ++i)for (int j = (i & 1) ^ 1; i * i + j * j <= n && j < i; j += 2)if (__gcd(i, j) == 1) {for (int k = 1; k * (i * i + j * j) <= n; ++k) {int a = k * (i * i - j * j), b = k * (2 * i * j);if (a + b + 1 <= n) {triple.emplace_back(a, b);if (b) triple.emplace_back(b, a);// 交换 (a, b)}}}for (int i = 1, x, y; i <= q; ++i) {scanf("%d%d", &x, &y);qx[x].emplace_back(y, i);qy[y].emplace_back(x, i);}for (auto& [a, b]: triple) line[n - a - b].emplace_back(1 + a, n - b);for (int i = n; i >= 1; --i) {for (auto& [l, r]: line[i]) yzh.modify(l, r, 1);for (auto& [y, p]: qx[i]) ans[p] += yzh.query(y);}yzh.clear();for (int i = 1; i <= n; ++i) line[i].clear();for (auto& [a, b]: triple) line[1 + a + b].emplace_back(1 + b, n - a);for (int i = 1; i <= n; ++i) {for (auto& [l, r]: line[i]) yzh.modify(l, r, 1);for (auto& [y, p]: qx[i]) ans[p] += yzh.query(y);}yzh.clear();for (int i = 1; i <= n; ++i) line[i].clear();for (auto& [a, b]: triple) line[1 + a + b].emplace_back(1 + a, n - b);for (int i = 1; i <= n; ++i) {for (auto& [l, r]: line[i]) yzh.modify(l, r, 1);for (auto& [y, p]: qy[i]) ans[p] += yzh.query(y);}yzh.clear();for (int i = 1; i <= n; ++i) line[i].clear();for (auto& [a, b]: triple) line[n - a - b].emplace_back(1 + b, n - a);for (int i = n; i >= 1; --i) {for (auto& [l, r]: line[i]) yzh.modify(l, r, 1);for (auto& [y, p]: qy[i]) ans[p] += yzh.query(y);}for (int i = 1; i <= q; ++i)printf("%d\n", ans[i]);return 0;
}

References


  1. Wikipedia, "Pythagorean Triple", Generating a Triple and Proof of Euclid's Formula。 ↩︎ ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/801396.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《AI系统:原理与架构》于华为HC大会2024正式发布

2024年9月21日,《AI系统:原理与架构》新书发布会在上海世博馆华为HC大会顺利举办。本书由华为昇腾技术专家、B站AI科普博主ZOMI酱和哈工大软件学院副院长苏统华教授联合编写,是领域内AI系统方面填补空白的重磅之作 发布会上,《AI系统:原理与架构》的作者、编辑代表分别介绍…

南沙C++信奥老师解一本通题:1372:小明的账单

​【题目描述】小明在一次聚会中,不慎遗失了自己的钱包,在接下来的日子,面对小明的将是一系列的补卡手续和堆积的账单… 在小明的百般恳求下,老板最终同意延缓账单的支付时间。可老板又提出,必须从目前还没有支付的所有账单中选出面额最大和最小的两张,并把他们付清。还没…

.Net Core 页面Tag Helpers不提示,颜色也没有变化

没弄明白具体哪里出的问题,按老外的说法,这俩截图中的选项切换一下,并且重启VS2022,就好了https://stackoverflow.com/questions/75558595/intellisense-in-razor-cshtml-files-not-working-visual-studio-2022

解决方案 | 为什么搜狗输入法打不出符号了以前我打平方?输入平方即可出现 的候选词

按照: 更多设置---------》属性设置------->高级------------->符号大全开启。

查询 B 站注册时间

有时候想看看自己玩 B 站多少年了,想知道自己什么时候注册的。此外,据说注销 B 站账户的话也得提供详细注册日期。有时候想看看自己玩 B 站多少年了,想知道自己什么时候注册的。 此外,据说注销 B 站账户的话也得提供详细注册日期。 ‍ 通过创作中心查看 登录网页版 B 站,点…

第01章_Java语言概述

1 Java 语言概述 1.1 Java 概述是 SUN (Stanford University Network,斯坦福大学网络公司 ) 1995年 推出的一门高级编程语言。 是一种面向 Internet 的编程语言。Java 一开始富有吸引力是因为 Java 程序可以在 Web浏览器 中运行。这些Java程序被称为 Java小程序 (applet),内嵌…

Android-kotlin相关构建下载慢的问题处理建议

我们在导入其他的android项目获取需要手动改变android的依赖版本比如gradle版本,kotlin版本等等,点击同步构建时会发现需要很长的时间,有时还会失去连接,这是因为我们在国内的网络访问外网又没有梯子的情况下导致的 下载是解决这种情况的一些建议 1.使用梯子(有更好,没有…

一个.NET开源、快速、低延迟的异步套接字服务器和客户端库

前言 最近有不少小伙伴在问:.NET有什么值得推荐的网络通信框架?今天大姚给大家分享一个.NET开源、免费(MIT License)、快速、低延迟的异步套接字服务器和客户端库:NetCoreServer。 项目介绍 NetCoreServer是一个.NET开源、免费(MIT License)、快速、低延迟的异步套接字服…

VMware vCenter Server 7.0U3s 发布下载,新增功能概览

VMware vCenter Server 7.0U3s 发布下载,新增功能概览VMware vCenter Server 7.0U3s 下载 - 集中管理 vSphere 环境 Server Management Software | vCenter | 集中管理 vSphere 环境 请访问原文链接:https://sysin.org/blog/vmware-vcenter-7-u3/,查看最新版。原创作品,转载…

读构建可扩展分布式系统:方法与实践11强一致性

强一致性1. 强一致性 1.1. 最终一致数据库通过跨多台机器分区和复制数据集来获得可扩展性,其代价是要跨副本维持强数据一致性以及允许冲突写入1.1.1. 在更新数据对象后,不同的客户端可能会看到该对象的旧值或新值,直到所有副本都收敛到最新值1.2. 另一类分布式数据库提供一种…

提升软件测试效率与灵活性:探索Mock测试的重要性

Mock测试是测试过程中的一种方法,用于替代那些难以构造或获取的对象,通过创建虚拟对象来进行测试。所谓难以构造的对象如何理解呢? 举例来说,像HttpServletRequest这样的对象需要在具有servlet容器环境的情况下才能创建和获取。而难以获取的对象则是指需要准备相关环境才能…

《机器人SLAM导航核心技术与实战》第1季:第9章_视觉SLAM系统

《机器人SLAM导航核心技术与实战》第1季:第9章_视觉SLAM系统 视频讲解【第1季】9.第9章_视觉SLAM系统-视频讲解【第1季】9.1.第9章_视觉SLAM系统_ORB-SLAM2算法(上)-视频讲解【第1季】9.1.第9章_视觉SLAM系统_ORB-SLAM2算法(下)-视频讲解【第1季】9.2.第9章_视觉SLAM系统_…