【LeetCode 1925. 统计平方和三元组的数目 题解】

2025 / 12 / 8 每日一题

首先,本原勾股数 ( a , b , c ) (a, b, c) (a,b,c) 有几个性质。

  • gcd ⁡ ( a , b , c ) = 1 , \gcd(a, b, c) = 1, gcd(a,b,c)=1,
  • gcd ⁡ ( a , b ) = gcd ⁡ ( a , c ) = gcd ⁡ ( b , c ) = 1 , \gcd(a, b)= \gcd(a, c) = \gcd(b, c) = 1, gcd(a,b)=gcd(a,c)=gcd(b,c)=1,
  • a , b a, b a,b 一定是一奇一偶 , , ,
  • 若 a a a 是奇数,则 gcd ⁡ ( c + b , c − b ) = 1. \gcd(c + b, c - b) = 1. gcd(c+b,c−b)=1.

本原勾股数的半通解(即 a a a 为奇数的解)是

{ a = u v , b = u 2 − v 2 2 , c = u 2 + v 2 2 . \begin{cases} a = uv, \\ b = \dfrac{u^2 - v^2}{2}, \\ c = \dfrac{u^2 + v^2}{2}. \end{cases} ⎩ ⎨ ⎧a=uv,b=2u2−v2,c=2u2+v2.

满足 u , v u, v u,v 都是奇数 且 1 ≤ v < u 1 \leq v < u 1≤v<u 且 gcd ⁡ ( u , v ) = 1 \gcd(u, v) = 1 gcd(u,v)=1。

这样对于 1 ≤ a , b , c ≤ n 1 \leq a, b, c \leq n 1≤a,b,c≤n 的勾股数对计算就可以做到 O ( n log ⁡ n ) O(n\log n) O(nlogn) 或者 O ( n ) O(n) O(n),最终的结果为

2 ⋅ ∑ 1 ≤ v < u , u , v odd gcd ⁡ ( u , v ) = 1 , u 2 + v 2 ≤ 2 n ⌊ 2 n u 2 + v 2 ⌋ . 2 \cdot \sum\limits_{\substack{1 \leq v < u, \ u, v \ \text{odd} \\ \gcd(u, v) = 1, \ u^2 + v^2 \leq 2n}} \left\lfloor \frac{2n}{u^2 + v^2} \right\rfloor. 2⋅1≤v<u, u,v oddgcd(u,v)=1, u2+v2≤2n∑⌊u2+v22n⌋.

求和号前面的 2 2 2 表示将 ( a , b , c ) (a, b, c) (a,b,c) 扩展为 ( b , a , c ) (b, a, c) (b,a,c)。


考虑优化上面的求和。

  • u < n 1 / 3 u < n^{1/3} u<n1/3 时,两层循环枚举的 ( u , v ) (u, v) (u,v) 个数为 O ( n 2 / 3 ) O(n^{2/3}) O(n2/3);
  • u ≥ n 1 / 3 u \geq n^{1/3} u≥n1/3,对于固定的 u u u,由于 2 n u 2 + v 2 < 2 n u 2 \dfrac{2n}{u^2 + v^2} < \dfrac{2n}{u^2} u2+v22n<u22n,所以枚举 v v v 的个数最多为 O ( n u 2 ) O\left(\dfrac{n}{u^2}\right) O(u2n),那么总共枚举的 ( u , v ) (u, v) (u,v) 个数就应该是 ∑ u = n 1 / 3 n 1 / 2 O ( n u 2 ) = ∫ n 1 / 3 n 1 / 2 O ( n u 2 ) d u = O ( n 2 / 3 − n 1 / 2 ) = O ( n 2 / 3 ) . \sum\limits_{u = n^{1/3}}^{n^{1/2}}O\left(\dfrac{n}{u^2}\right) = \int_{n^{1/3}}^{n^{1/2}}O\left(\dfrac{n}{u^2}\right)du = O\left(n^{2/3} - n^{1/2}\right) = O\left(n^{2/3}\right). u=n1/3∑n1/2O(u2n)=∫n1/3n1/2O(u2n)du=O(n2/3−n1/2)=O(n2/3).

套用整除分块的思想,对于固定的 u u u,我们考虑如何快速求出 v v v 的右边界,使得 ⌊ 2 n u 2 + v 2 ⌋ = ⌊ 2 n u 2 + l 2 ⌋ . \left\lfloor \frac{2n}{u^2 + v^2} \right\rfloor = \left\lfloor \frac{2n}{u^2 + l^2} \right\rfloor. ⌊u2+v22n⌋=⌊u2+l22n⌋.

设 k = ⌊ 2 n u 2 + v 2 ⌋ k = \left\lfloor \dfrac{2n}{u^2 + v^2} \right\rfloor k=⌊u2+v22n⌋,则有

k ≤ 2 n u 2 + v 2 , k \leq \frac{2n}{u^2 + v^2}, k≤u2+v22n,

因此有

u 2 + v 2 ≤ 2 n k , u^2 + v^2 \leq \frac{2n}{k}, u2+v2≤k2n,

那么就有

v ≤ ⌊ ⌊ 2 n k ⌋ − u 2 ⌋ , v \leq \left\lfloor \sqrt{\left\lfloor \frac{2n}{k} \right\rfloor} - u^2 \right\rfloor, v≤⌊⌊k2n⌋ −u2⌋,

于是取

r = min ⁡ ( ⌊ ⌊ 2 n k ⌋ − u 2 ⌋ , u − 1 ) r = \min\left( \left\lfloor \sqrt{\left\lfloor \frac{2n}{k} \right\rfloor} - u^2 \right\rfloor, \ u - 1 \right) r=min(⌊⌊k2n⌋ −u2⌋, u−1)

于是同一类 v v v 就在 [ l , r ] [l, r] [l,r],它们都满足

k = ⌊ 2 n u 2 + v 2 ⌋ , k = \left\lfloor \frac{2n}{u^2 + v^2} \right\rfloor, k=⌊u2+v22n⌋,

其中 l l l 的初值为 1 1 1。


现在问题转化为,对于固定的奇数 u u u,以及 v v v 的取值范围 [ l , r ] [l, r] [l,r],快速求出有多少个奇数 v v v 满足 gcd ⁡ ( u , v ) = 1 \gcd(u, v) = 1 gcd(u,v)=1。

采用前缀和的思想,我们将 f ( u , m ) f(u, m) f(u,m) 定义为:

  • 对于固定的奇数 u u u,求有多少个奇数 v ∈ [ 1 , m ] v \in [1, m] v∈[1,m] 满足 gcd ⁡ ( u , v ) = 1 \gcd(u, v) = 1 gcd(u,v)=1。

那么上面的问题就是 f ( u , r ) − f ( u , l − 1 ) f(u, r) - f(u, l - 1) f(u,r)−f(u,l−1)。

这样这一段对答案的贡献就是 k ⋅ ( f ( u , r ) − f ( u , l − 1 ) ) k \cdot (f(u, r) - f(u, l - 1)) k⋅(f(u,r)−f(u,l−1))。


接着考虑 f ( u , m ) f(u, m) f(u,m) 的计算。

再将其拆开,我们只需要计算:

  • 对于固定的奇数 u u u,求有多少个 v ∈ [ 1 , m ] v \in [1, m] v∈[1,m] 满足 gcd ⁡ ( u , v ) = 1 \gcd(u, v) = 1 gcd(u,v)=1,答案记为 s 0 , s_0, s0,
  • 对于固定的奇数 u u u,求有多少个偶数 v ∈ [ 1 , m ] v \in [1, m] v∈[1,m] 满足 gcd ⁡ ( u , v ) = 1 \gcd(u, v) = 1 gcd(u,v)=1,答案记为 s 2 . s_2. s2.

那么 f ( u , m ) = s 0 − s 2 . f(u, m) = s_0 - s_2. f(u,m)=s0−s2.

将 g ( u , m ) g(u, m) g(u,m) 定义为:

  • 对于固定的奇数 u u u,求有多少个 v ∈ [ 1 , m ] v \in [1, m] v∈[1,m] 满足 gcd ⁡ ( u , v ) = 1 \gcd(u, v) = 1 gcd(u,v)=1。

由于 u u u 是奇数, v v v 是偶数,因此在求解 s 2 s_2 s2 时,有 gcd ⁡ ( u , v ) = gcd ⁡ ( u , 2 v ′ ) = gcd ⁡ ( u , v ′ ) \gcd(u, v) = \gcd(u, 2v') = \gcd(u, v') gcd(u,v)=gcd(u,2v′)=gcd(u,v′)。

因此有 f ( u , m ) = g ( u , m ) − g ( u , ⌊ m 2 ⌋ ) f(u, m) = g(u, m) - g\left(u, \left\lfloor \dfrac{m}{2} \right\rfloor \right) f(u,m)=g(u,m)−g(u,⌊2m⌋)。


最后考虑 g ( u , m ) g(u, m) g(u,m) 的计算。

将 u u u 进行质因数分解得到 m = p 1 α 1 p 2 α 2 ⋯ p k α k m = p_1^{\alpha_1}p_2^{\alpha_2}\cdots p_k^{\alpha_k} m=p1α1p2α2⋯pkαk,记 P = { p 1 , p 2 , ⋯   , p k } P = \lbrace p_1, p_2, \cdots, p_k \rbrace P={p1,p2,⋯,pk}。

首先 [ 1 , m ] [1, m] [1,m] 内是 p i p_i pi 倍数的都是不满足要求的,要扣除,但直接扣掉 ∑ i = 1 k ⌊ m p i ⌋ \sum\limits_{i = 1}^{k}\left\lfloor \dfrac{m}{p_i} \right\rfloor i=1∑k⌊pim⌋ 会多扣,这就启发我们用容斥原理。

所以有

g ( u , m ) = ∑ S ⊂ P ( − 1 ) ∣ S ∣ ⌊ n ∏ S ⌋ = ∑ d ∣ u μ ( d ) ⌊ n d ⌋ , g(u, m) = \sum\limits_{S \subset P}(-1)^{|S|}\left\lfloor \frac{n}{\prod S} \right\rfloor = \sum\limits_{d \mid u}\mu(d)\left\lfloor \frac{n}{d} \right\rfloor, g(u,m)=S⊂P∑(−1)∣S∣⌊∏Sn⌋=d∣u∑μ(d)⌊dn⌋,

其中 S S S 表示 P P P 的子集, d ∣ u d \mid u d∣u 表示 d d d 为 u u u 的因数, μ ( ⋅ ) \mu(\cdot) μ(⋅) 表示莫比乌斯函数,定义如下:

μ ( n ) = { 1 , n = 1 ( − 1 ) k , n = p 1 p 2 ⋯ p k 0 , n 有大于 1 的平方因子 \mu(n) = \begin{cases} 1,& n = 1 \\ (-1)^k,& n = p_1p_2\cdots p_k \\ 0,& n \ 有大于 \ 1 \ 的平方因子 \end{cases} μ(n)=⎩ ⎨ ⎧1,(−1)k,0,n=1n=p1p2⋯pkn 有大于 1 的平方因子

而要分析 g ( u , m ) g(u, m) g(u,m) 的大致时间复杂度,我们就要知道每个数可能有多少因数。

考虑下面的求和 S ( n ) = ∑ i = 1 n τ ( i ) S(n) = \sum\limits_{i = 1}^{n}\tau(i) S(n)=i=1∑nτ(i),其中 τ ( i ) \tau(i) τ(i) 表示 i i i 的因数个数。

Dirichlet 双曲线法 得到

S ( n ) = ∑ x ≤ n ∑ y ≤ n / x 1 ⋅ 1 + ∑ y ≤ n ∑ x ≤ n / y 1 ⋅ 1 − ∑ x ≤ n ∑ y ≤ n 1 ⋅ 1 = 2 ∑ x ≤ n ⌊ n x ⌋ − ⌊ n ⌋ 2 = 2 ∑ x ≤ n ( n x − O ( 1 ) ) − O ( n ) = 2 ( n ∑ x ≤ n 1 x − ∑ x ≤ n O ( 1 ) ) − O ( n ) = 2 ( O ( n log ⁡ n ) − O ( n ) ) − O ( n ) = O ( n log ⁡ n − n − n ) = O ( n log ⁡ n ) . \begin{align*} S(n) &= \sum\limits_{x \leq \sqrt{n}}\sum\limits_{y \leq n / x} 1 \cdot 1 + \sum\limits_{y \leq \sqrt{n}}\sum\limits_{x \leq n / y} 1 \cdot 1 - \sum\limits_{x \leq \sqrt{n}}\sum\limits_{y \leq \sqrt{n}} 1 \cdot 1 \\ &= 2 \sum\limits_{x \leq \sqrt{n}}\left\lfloor \frac{n}{x} \right\rfloor - \lfloor \sqrt{n} \rfloor^2 \\ &= 2 \sum\limits_{x \leq \sqrt{n}}\left(\frac{n}{x} - O(1)\right) - O(n) \\ &= 2 \left( n \sum\limits_{x \leq \sqrt{n}}\frac{1}{x} - \sum\limits_{x \leq \sqrt{n}}O(1) \right) - O(n) \\ &= 2 \left( O(n\log \sqrt{n}) - O(\sqrt{n}) \right) - O(n) \\ &= O(n\log n - \sqrt{n} - n) \\ &= O(n\log n). \end{align*} S(n)=x≤n ∑y≤n/x∑1⋅1+y≤n ∑x≤n/y∑1⋅1−x≤n ∑y≤n ∑1⋅1=2x≤n ∑⌊xn⌋−⌊n ⌋2=2x≤n ∑(xn−O(1))−O(n)=2 nx≤n ∑x1−x≤n ∑O(1) −O(n)=2(O(nlogn )−O(n ))−O(n)=O(nlogn−n −n)=O(nlogn).

因此有

S ( n ) n = O ( log ⁡ n ) , \frac{S(n)}{n} = O(\log n), nS(n)=O(logn),

也就是对于 ∀ x ∈ [ 1 , n ] \forall x \in [1, n] ∀x∈[1,n],均摊来看, x x x 的因数个数大致为 O ( log ⁡ n ) O(\log n) O(logn)。


μ ( ⋅ ) \mu(\cdot) μ(⋅) 可以用埃筛 O ( n log ⁡ n ) = O ( n log ⁡ n ) O(\sqrt{n}\log \sqrt{n}) = O(\sqrt{n}\log n) O(n logn )=O(n logn) 预处理。

具体来说,当 n > 1 n > 1 n>1 时,有 ∑ d ∣ n μ ( d ) = 0 \sum\limits_{d \mid n}\mu(d) = 0 d∣n∑μ(d)=0,所以

μ ( n ) = − ∑ d ∣ n , d < n μ ( d ) . \mu(n) = -\sum\limits_{\substack{d \mid n, \\ d < n}}\mu(d). μ(n)=−d∣n,d<n∑μ(d).


总时间复杂度为 O ( n 2 / 3 log ⁡ n + n log ⁡ n ) = O ( n 2 / 3 log ⁡ n ) O(n^{2/3}\log n + \sqrt{n}\log n) = O(n^{2/3}\log n) O(n2/3logn+n logn)=O(n2/3logn)。

  • 其中前一个 log ⁡ n \log n logn 表示枚举 u u u 的因数。

特别的,取 B = ( n log ⁡ n ) 1 / 3 B = (n\log n)^{1/3} B=(nlogn)1/3,当 u < B u < B u<B 时使用暴力枚举,当 u ≥ B u \geq B u≥B 时使用上述分块优化,时间复杂度可优化到 O ( ( n log ⁡ n ) 2 / 3 ) O((n\log n)^{2/3}) O((nlogn)2/3)。


参考文献灵神的题解

下面的代码写的是复杂度 O ( ( n log ⁡ n ) 2 / 3 ) O((n\log n)^{2/3}) O((nlogn)2/3) 版本的。

cpp 复制代码
constexpr int N = 24;

vector<vector<int>> divs;
vector<int> mu;

auto init = []() {
    divs.resize(N + 1);
    mu.resize(N + 1);
    mu[1] = 1;
    for (int i = 1; i <= N; i++) {
        for (int j = i + i; j <= N; j += i) {
            mu[j] -= mu[i];
        }
    }
    for (int i = 1; i <= N; i++) {
        if (mu[i]) {
            for (int j = i; j <= N; j += i) {
                divs[j].push_back(i);
            }
        }
    }
    return 0;
}();

class Solution {
public:
    int countTriples(int n) {
        int cn = cbrt(n * log(n));
        int ans = 0;
        for (int u = 3; u * u < 2 * n; u += 2) {
            if (u < cn) {
                for (int v = 1; v < u; v += 2) {
                    if (gcd(u, v) == 1) {
                        ans += (2 * n) / (u * u + v * v);
                    }
                }
                continue;
            }
            auto calc_coprime = [&](int m) {
                auto calc = [&](int m) {
                    int res = 0;
                    for (int d: divs[u]) {
                        res += mu[d] * (m / d);
                    }
                    return res;
                };
                return calc(m) - calc(m / 2);
            };
            for (int l = 1, r; l < u and u * u + l * l <= 2 * n; l = r + 1) {
                int k = (2 * n) / (u * u + l * l);
                r = std::min<int>(sqrt((2 * n) / k - u * u), u - 1);
                ans += k * (calc_coprime(r) - calc_coprime(l - 1));
            }
        }
        return ans * 2;
    }
};

实际上要预处理一下 ( n log ⁡ n ) 1 / 3 (n\log n)^{1/3} (nlogn)1/3 以内的互质数对,可以用更相减损术。

gcd ⁡ ( i , j ) = { gcd ⁡ ( i , j − i ) , i ≤ j gcd ⁡ ( j , i ) , i > j . \gcd(i, j) = \begin{cases} \gcd(i, j - i),& i \leq j \\ \gcd(j, i),& i > j \end{cases}. gcd(i,j)={gcd(i,j−i),gcd(j,i),i≤ji>j.

初始值为 gcd ⁡ ( i , 0 ) = i \gcd(i, 0) = i gcd(i,0)=i。

相关推荐
ytttr87317 小时前
隐马尔可夫模型(HMM)MATLAB实现范例
开发语言·算法·matlab
AlenTech17 小时前
160. 相交链表 - 力扣(LeetCode)
数据结构·leetcode·链表
点云SLAM17 小时前
凸优化(Convex Optimization)理论(1)
人工智能·算法·slam·数学原理·凸优化·数值优化理论·机器人应用
会周易的程序员18 小时前
多模态AI 基于工业级编译技术的PLC数据结构解析与映射工具
数据结构·c++·人工智能·单例模式·信息可视化·架构
jz_ddk18 小时前
[学习] 卫星导航的码相位与载波相位计算
学习·算法·gps·gnss·北斗
放荡不羁的野指针18 小时前
leetcode150题-动态规划
算法·动态规划
sin_hielo18 小时前
leetcode 1161(BFS)
数据结构·算法·leetcode
一起努力啊~18 小时前
算法刷题-二分查找
java·数据结构·算法
水月wwww18 小时前
【算法设计】动态规划
算法·动态规划
码农水水19 小时前
小红书Java面试被问:Online DDL的INSTANT、INPLACE、COPY算法差异
算法