📜  GCD等于K的范围[L,R]中的四元组计数(1)

📅  最后修改于: 2023-12-03 15:00:53.200000             🧑  作者: Mango

GCD 等于 K 的范围 [L, R] 中的四元组计数

在数学中,最大公约数(GCD)是两个或多个整数的最大公因数。在本文中,我们将介绍如何计算一个范围内 GCD 等于 K 的四元组数量。

问题描述

给定四个整数 L,R,K,N,计算有多少个四元组 (a, b, c, d),其中 L ≤ a ≤ b ≤ R,L ≤ c ≤ d ≤ R,且 GCD(a, b, c, d) = K,且 a, b, c, d 均为 N 的倍数。

思路

我们可以暴力地枚举所有四元组并计算它们的 GCD 值,然后统计满足条件的四元组数量。但这个算法时间复杂度是 O(N^4),无法通过本题。

我们可以使用数学公式来简化计算。我们可以使用容斥原理计算至少有一个数的 GCD 是 K 的四元组数量,然后用同样的方法计算恰有两个数、三个数和四个数的 GCD 是 K 的四元组数量。最后将它们相加得到最终结果。

假设存在一个四元组 (a, b, c, d) 满足 L ≤ a ≤ b ≤ R,L ≤ c ≤ d ≤ R,且 GCD(a, b, c, d) = K。设 x = a/K,y = b/K,z = c/K,w = d/K,则 x, y, z, w 都是整数,且它们的 GCD 值仍然是 K。此外,我们还有 1 ≤ x ≤ y ≤ R/K 和 1 ≤ z ≤ w ≤ R/K。

我们定义 f(x, y, z, w) 表示 x ≤ y,z ≤ w,且它们的 GCD 值是 K 的四元组数量。由于 a, b, c, d 是 N 的倍数,因此 x, y, z, w 也都是 N 的倍数。我们可以将它们分别除以 N(等价于向下取整)得到 u = x/N,v = y/N,s = z/N,t = w/N,此时 u, v, s, t 仍然都是整数。

假设 N = K,我们可以将四元组中的每个数除以 K,那么得到的新的四元组 (x', y', z', w') 满足 x' ≤ y',z' ≤ w',且它们的 GCD 值是 1。此时我们可以使用数学公式计算 f(u, v, s, t) 的值。

对于任意给定的四个整数 x, y, z, w,我们可以定义 g(x, y, z, w) 表示 x ≤ y,z ≤ w,且它们的 GCD 值是 1 的四元组数量。我们可以使用 Inclusion-Exclusion 原理计算 f(u, v, s, t) 的值,具体来说,有:

f(u, v, s, t) = ∑ᴸ g(i, v, j, t) - ∑ᴸ g(i, v, j, s-1) - ∑ᴸ g(i, u-1, j, t) + ∑ᴸ g(i, u-1, j, s-1)

其中 L = 1 到 R/K,g(i, j, k, l) 表示 GCD(i, j, k, l) = 1 且 i ≤ j,k ≤ l 的四元组数量。

代码实现

为了计算 g(i, j, k, l) 的值,我们可以使用线性筛法计算出 1 到 R/K 的所有质数以及每个数的欧拉函数值。欧拉函数相当于在给定的整数范围内计算与其互质的数的数量,即 g(i, j, k, l) 的值。

def gcd(a, b):
    while b:
        a, b = b, a % b
    return a

def count_quadruples(L, R, K, N):
    MAXN = R // N + 1
    prime = [True] * MAXN
    phi = list(range(MAXN))
    for p in range(2, MAXN):
        if prime[p]:
            for i in range(p, MAXN, p):
                prime[i] = False
                phi[i] = phi[i] // p * (p - 1)
    ans = 0
    for u in range(1, MAXN):
        for v in range(u, MAXN):
            for s in range(1, MAXN):
                for t in range(s, MAXN):
                    if gcd(u, v, s, t) != 1:
                        continue
                    a, b, c, d = u*N*K, v*N*K, s*N*K, t*N*K
                    if a > R or c > R:
                        continue
                    if b < L or d < L:
                        continue
                    L1, R1 = max((L - 1) // a + 1, (L - 1) // b + 1), (R // a) + (R // b)
                    L2, R2 = max((L - 1) // c + 1, (L - 1) // d + 1), (R // c) + (R // d)
                    L3, R3 = max((L - 1) // (K*N)), (s - 1) * v // u + 1
                    R3 = min(R3, t * v // u)
                    L4, R4 = max((L - 1) // (K*N)), s * (v - 1) // u + 1
                    R4 = min(R4, (t - 1) * (v - 1) // u)
                    if L1 <= R1 and L2 <= R2 and L3 <= R3 and L4 <= R4:
                        ans += (-1) ** (u + v + s + t) * phi[u] * phi[v] * phi[s] * phi[t] * (R1 - L1 + 1) * (R2 - L2 + 1) * (R3 - L3 + 1) * (R4 - L4 + 1)
    return ans

代码中,我们使用四重循环枚举所有符合要求的四元组 (u, v, s, t),并根据恰好有 i 个数的 GCD 是 1 的四元组数量计算 f(u, v, s, t) 的值。

我们首先判断是否有 GCD(u, v, s, t) = 1,如果不满足此条件,则四个数的 GCD 必然是 K 的倍数,无法满足条件。然后我们计算 a, b, c, d 的值,并判断它们是否符合范围要求。接下来计算 L1, R1, L2, R2, L3, R3, L4, R4 的值,这四个区间分别对应至少有一个数、恰好有两个数、恰好有三个数和恰好有四个数的 GCD 是 K 的四元组数量。最后我们根据以上公式计算 f(u, v, s, t) 的值,并将其累加到答案中。