📅  最后修改于: 2023-12-03 14:57:26.597000             🧑  作者: Mango
本任务的目标是给定一个整数数组 nums
和一个正整数 k
,计算有多少对 (i,j)
满足 1 <= i < j <= nums.length
,使得 nums[i]*nums[j]
的值的 k
次方是一个正整数。
我们首先要明确一点,如果两个数的乘积是一个正整数,那么这两个数必须同时是正数或者同时是负数。因此,我们可以将数组 nums
按照正数和负数分开来考虑。
对于正数的情况,我们可以先将数组 nums
排序,然后使用双指针的方式寻找乘积为正整数的数对。
对于负数的情况,我们同样可以先将数组 nums
排序,并将负数排在数组的前面。接着对于每一个正数,在负数中查找与其乘积是正整数的数的个数,这可以使用二分查找来实现。
需要注意的是,如果 k
的值非常大,那么计算乘积的时候可能会出现溢出的情况。因此,我们可以考虑将乘积表示为质因数的形式,并计算每一个质因子的 k
次幂。这种方法可以避免溢出的问题。
from typing import List
from bisect import bisect_left, bisect_right
def factorize(n: int) -> List[int]:
# 将 n 分解成质因数的形式
factors = []
d = 2
while d * d <= n:
while (n % d) == 0:
factors.append(d)
n //= d
d += 1
if n > 1:
factors.append(n)
return factors
def countPairs(nums: List[int], k: int) -> int:
n = len(nums)
pos_nums = sorted([x for x in nums if x > 0])
neg_nums = sorted([x for x in nums if x < 0])
pos_count = 0
for i in range(len(pos_nums)):
j = bisect_left(pos_nums, (k-1) // (pos_nums[i]**k)) # 计算乘积的倒数的下取整
pos_count += len(pos_nums[j:])
neg_count = 0
for i in range(len(neg_nums)):
j = bisect_left(neg_nums, -(k-1) // abs(neg_nums[i])) # 计算乘积的倒数的上取整
neg_count += len(neg_nums[j:])
ans = pos_count * (n - pos_count) + neg_count * (n - neg_count)
for i in range(len(pos_nums)):
factors = factorize(pos_nums[i])
if len(factors) > 0 and max(factors) ** k <= (10**9):
j = bisect_left(neg_nums, (10**9) // max(factors) ** k)
ans += (len(neg_nums) - j) * (n - pos_count)
return ans
import java.util.*;
class Solution {
public List<Integer> factorize(int n) {
// 将 n 分解成质因数的形式
List<Integer> factors = new ArrayList<>();
int d = 2;
while (d * d <= n) {
while (n % d == 0) {
factors.add(d);
n /= d;
}
d += 1;
}
if (n > 1) {
factors.add(n);
}
return factors;
}
public int countPairs(int[] nums, int k) {
int n = nums.length;
int[] posNums = Arrays.stream(nums).filter(x -> x > 0).sorted().toArray();
int[] negNums = Arrays.stream(nums).filter(x -> x < 0).sorted().toArray();
int posCount = 0;
for (int i = 0; i < posNums.length; i++) {
int j = Arrays.binarySearch(posNums, (k-1) / power(posNums[i], k));
if (j < 0) {
j = -j-1;
}
posCount += posNums.length - j;
}
int negCount = 0;
for (int i = 0; i < negNums.length; i++) {
int j = Arrays.binarySearch(negNums, -(k-1) / Math.abs(negNums[i]));
if (j < 0) {
j = -j-1;
}
negCount += negNums.length - j;
}
int ans = posCount * (n - posCount) + negCount * (n - negCount);
for (int i = 0; i < posNums.length; i++) {
List<Integer> factors = factorize(posNums[i]);
if (factors.size() > 0 && power(max(factors), k) <= 1e9) {
int j = Arrays.binarySearch(negNums, (int) (1e9 / power(max(factors), k)));
if (j < 0) {
j = -j-1;
}
ans += (negNums.length - j) * (n - posCount);
}
}
return ans;
}
private int power(int x, int k) {
int res = 1;
while (k > 0) {
if ((k & 1) != 0) {
if (res > 1e9 / x) {
return -1;
}
res *= x;
}
if (k > 1 && x > 1e9 / x) {
return -1;
}
x *= x;
k >>= 1;
}
return res;
}
private int max(List<Integer> nums) {
int res = 0;
for (int num : nums) {
res = Math.max(res, num);
}
return res;
}
}
该问题虽然涉及到一些数学知识,但是思路比较清晰,实现较为简单,主要考察对二分查找的掌握程度。对于用 Python 的程序员来说,可以使用 bisect 模块来简化二分查找;对于用 Java 的程序员来说,则可以使用 Arrays.binarySearch() 函数。