📜  Q查询中LCM不等于其乘积的N个对的计数(1)

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

题目描述

给定 $N$ 个正整数 $a_1,a_2,...,a_N$,求有多少组 $(i,j)$ 满足 $i<j$ 且 $\operatorname{lcm}(a_i,a_j)\neq a_i\times a_j$。

解法

首先考虑 $\operatorname{lcm}(a_i,a_j)$ 和 $a_i\times a_j$ 哪些时候相等。根据 $\operatorname{lcm}$ 的定义,$\operatorname{lcm}(a_i,a_j)$ 是最小的正整数 $m$,满足 $a_i| m$ 且 $a_j | m$。因此,当 $a_i$ 和 $a_j$ 互质时,$\operatorname{lcm}(a_i,a_j) = a_i \times a_j$。因此,我们只需要考虑 $a_i$ 和 $a_j$ 不互质的情况。

记 $\gcd(a_i,a_j) = t$,其中 $t$ 是 $a_i$ 和 $a_j$ 的最大公约数。令 $a_i = tu$,$a_j = tv$,其中 $\gcd(u,v) = 1$。显然,$\operatorname{lcm}(a_i,a_j) = tuv$,$a_i\times a_j = t^2uv$,因此只需计算有多少组 $(i,j)$ 满足 $a_i$ 和 $a_j$ 不互质且 $uv\neq t$。

令 $s_n$ 表示正整数中所有因子个数为 $n$ 的数的个数。显然,$s_n$ 可以通过线性筛求出。对于每个 $a_i$,我们把 $a_i$ 的因子们分成两个集合,一个集合中的因子都和 $a_j$ 的因子中至少有一个因子相同,另一个集合中的因子都和 $a_j$ 的因子中没有任何一个因子相同。则 $a_i$ 和 $a_j$ 不互质的充要条件是,$a_j$ 的因子中必须存在一个在第一个集合中的因子,或者 $a_j$ 的因子中必须存在一个在第二个集合中的因子。

我们可以对于每个 $a_i$,线性预处理出其所有因子均在第一个集合中的数的个数 $f_i$,以及其所有因子均在第二个集合中的数的个数 $g_i$。对于任意两个 $a_i$ 和 $a_j$,我们统计其对答案的贡献如下:$$\begin{aligned} \text{cnt} &= (d_i-f_i)(d_j-g_j) + (d_j-f_j)(d_i-g_i) \ &+ (f_i-f_{i+1})(g_j-g_{j+1}) + (f_j-f_{j+1})(g_i-g_{i+1}) \end{aligned}$$ 其中 $d_i$ 表示 $a_i$ 的因子个数,$f_{i+1}-f_i$ 和 $g_{i+1}-g_i$ 分别表示所有因子个数为 $i+1$ 的数在第一个和第二个集合中的个数差。第一项和第二项分别统计 $a_i$ 和 $a_j$ 的因子中都在第一个集合中的数的个数乘积以及都在第二个集合中的数的个数乘积;第三项和第四项分别统计存在一个在第一个集合中的因子或存在一个在第二个集合中的因子的情况。显然,第三项和第四项中出现 $i+1$ 的原因是我们枚举了 $t=\gcd(a_i,a_j)$,而 $t$ 的因子个数为 $i+1$。

代码实现
def count_pairs(n: int, a: List[int]) -> int:
    s = [0] * (max(a) + 1)
    for i in range(1, len(s)):
        for j in range(i, len(s), i):
            s[j] += 1

    f = [0] * (max(a) + 1)
    g = [0] * (max(a) + 1)
    for x in a:
        for y in range(1, int(x ** 0.5) + 1):
            if x % y != 0:
                continue
            if y ** 2 != x:
                f[y] += 1
                g[x // y] += 1
            else:
                f[y] += 1

    ans = 0
    for i in range(n):
        for j in range(i + 1, n):
            t = math.gcd(a[i], a[j])
            cnt = (s[t] - f[t]) * (s[t] - g[t]) + f[t] * g[t]
            cnt -= (f[t + 1] - f[t]) * (g[t + 1] - g[t])
            cnt -= (f[t] - f[t + 1]) * (g[t] - g[t + 1])
            ans += cnt

    return ans

代码使用了线性筛求出了 $s_n$。接着对于每个 $a_i$,预处理出了其在第一个集合和第二个集合中的个数,以及对于任意两个 $a_i$ 和 $a_j$,统计了其对答案的贡献。最后将所有的贡献加起来即可求出答案。