📌  相关文章
📜  乘积为两个整数的平方差的子序列的计数(1)

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

乘积为两个整数的平方差的子序列的计数

问题描述

给定一个整数序列 $a_1, a_2,\dots, a_n$,请计算其中有多少子序列乘积为两个整数的平方差,即 $a_i\times a_j = k^2\pm 1$($i<j$,$k$ 是一个整数)。

解决思路
思路一

我们可以对于每一个满足 $a_i\times a_j = k^2\pm 1$ 的 $(i,j,k)$,都记下以 $a_i$ 为第一个数,$a_j$ 为第二个数的子序列数。最后将所有的子序列数相加即可。

需要注意的是,若 $a_i$ 和 $a_j$ 均大于 $1$,那么 $a_i$ 和 $a_j$ 中一定有一个数是奇数,一个数是偶数。因此可以对于每个奇数 $x$,统计出序列中有多少偶数满足 $x\times a_j = k^2\pm 1$;对于每个偶数 $x$,统计出序列中有多少奇数满足 $x\times a_j = k^2\pm 1$。

这个做法的时间复杂度为 $O(n^2\log n)$,可以通过 50 分的测试点。

思路二

我们仔细分析一下这个问题。设 $\Delta=a_i-a_j$,则 $a_i-a_j = \frac{k^2-1}{a_i+a_j}$,因此 $a_i+a_j$ 一定是 $\Delta$ 的因子。

我们可以对于每个 $i$,考虑 $a_i$ 的因子有哪些,然后枚举 $\Delta$ 找到 $j$,统计个数。需要注意的是 $a_i=a_j$ 的情况。

这个做法的时间复杂度为 $O(n\sqrt{a_n})$,可以通过本题。

参考代码

思路一的参考代码:

n = int(input())
a = list(map(int, input().split()))

ans = 0
for i in range(n):
    cnt_odd = {1: 1}
    cnt_even = {}
    for j in range(i+1, n):
        if a[j] % 2 == 0:
            if cnt_odd.get(a[j]//2):
                ans += cnt_odd[a[j]//2]
            if cnt_even.get(a[j]//2):
                ans += cnt_even[a[j]//2]
            cnt_even[a[j]] = cnt_even.get(a[j], 0) + 1
        else:
            if cnt_even.get((a[j]-1)//2):
                ans += cnt_even[(a[j]-1)//2]
            if cnt_odd.get((a[j]-1)//2):
                ans += cnt_odd[(a[j]-1)//2]
            cnt_odd[a[j]] = cnt_odd.get(a[j], 0) + 1

print(ans)

思路二的参考代码:

n = int(input())
a = list(map(int, input().split()))

ans = 0
for i in range(n):
    cnt = {}
    for j in range(i+1, n):
        delta = a[i] - a[j]
        if delta == 0:
            ans += 1
        else:
            divisor = 1
            while divisor * divisor <= delta:
                if delta % divisor == 0:
                    if cnt.get(divisor) is not None and a[i] * a[j] == divisor * divisor + a[i] * a[j] // divisor:
                        ans += cnt[divisor]
                    if cnt.get(delta//divisor) is not None and a[i] * a[j] == (delta//divisor) * (delta//divisor) + a[i] * a[j] // (delta//divisor):
                        ans += cnt[delta//divisor]
                divisor += 1
        cnt[a[j]] = cnt.get(a[j], 0) + 1

print(ans)