comments | difficulty | edit_url | tags | ||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
true |
困难 |
|
给你一个长度为 n
的整数数组 nums
和一个整数数组 queries
。
gcdPairs
表示数组 nums
中所有满足 0 <= i < j < n
的数对 (nums[i], nums[j])
的 最大公约数 升序 排列构成的数组。
对于每个查询 queries[i]
,你需要找到 gcdPairs
中下标为 queries[i]
的元素。
请你返回一个整数数组 answer
,其中 answer[i]
是 gcdPairs[queries[i]]
的值。
gcd(a, b)
表示 a
和 b
的 最大公约数 。
示例 1:
输入:nums = [2,3,4], queries = [0,2,2]
输出:[1,2,2]
解释:
gcdPairs = [gcd(nums[0], nums[1]), gcd(nums[0], nums[2]), gcd(nums[1], nums[2])] = [1, 2, 1]
.
升序排序后得到 gcdPairs = [1, 1, 2]
。
所以答案为 [gcdPairs[queries[0]], gcdPairs[queries[1]], gcdPairs[queries[2]]] = [1, 2, 2]
。
示例 2:
输入:nums = [4,4,2,1], queries = [5,3,1,0]
输出:[4,2,1,1]
解释:
gcdPairs
升序排序后得到 [1, 1, 1, 2, 2, 4]
。
示例 3:
输入:nums = [2,2], queries = [0,0]
输出:[2,2]
解释:
gcdPairs = [2]
。
提示:
2 <= n == nums.length <= 105
1 <= nums[i] <= 5 * 104
1 <= queries.length <= 105
0 <= queries[i] < n * (n - 1) / 2
我们可以预处理得到数组
我们用
- 计算数组
$\textit{nums}$ 中$i$ 的倍数的出现次数$v$ ,那么从这些元素中任选两个元素组成的数对一定满足最大公约数是$i$ 的倍数,即$\textit{cntG}[i]$ 需要增加$v \times (v - 1) / 2$ ; - 我们需要排除最大公约数是
$i$ 的倍数且大于$i$ 的数对,因此,对于$i$ 的倍数$j$ ,我们需要减去$\textit{cntG}[j]$ 。
以上需要我们从大到小遍历
最后,我们计算数组
时间复杂度
class Solution:
def gcdValues(self, nums: List[int], queries: List[int]) -> List[int]:
mx = max(nums)
cnt = Counter(nums)
cnt_g = [0] * (mx + 1)
for i in range(mx, 0, -1):
v = 0
for j in range(i, mx + 1, i):
v += cnt[j]
cnt_g[i] -= cnt_g[j]
cnt_g[i] += v * (v - 1) // 2
s = list(accumulate(cnt_g))
return [bisect_right(s, q) for q in queries]
class Solution {
public int[] gcdValues(int[] nums, long[] queries) {
int mx = Arrays.stream(nums).max().getAsInt();
int[] cnt = new int[mx + 1];
long[] cntG = new long[mx + 1];
for (int x : nums) {
++cnt[x];
}
for (int i = mx; i > 0; --i) {
int v = 0;
for (int j = i; j <= mx; j += i) {
v += cnt[j];
cntG[i] -= cntG[j];
}
cntG[i] += 1L * v * (v - 1) / 2;
}
for (int i = 2; i <= mx; ++i) {
cntG[i] += cntG[i - 1];
}
int m = queries.length;
int[] ans = new int[m];
for (int i = 0; i < m; ++i) {
ans[i] = search(cntG, queries[i]);
}
return ans;
}
private int search(long[] nums, long x) {
int n = nums.length;
int l = 0, r = n;
while (l < r) {
int mid = l + r >> 1;
if (nums[mid] > x) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
}
}
class Solution {
public:
vector<int> gcdValues(vector<int>& nums, vector<long long>& queries) {
int mx = ranges::max(nums);
vector<int> cnt(mx + 1);
vector<long long> cntG(mx + 1);
for (int x : nums) {
++cnt[x];
}
for (int i = mx; i; --i) {
long long v = 0;
for (int j = i; j <= mx; j += i) {
v += cnt[j];
cntG[i] -= cntG[j];
}
cntG[i] += 1LL * v * (v - 1) / 2;
}
for (int i = 2; i <= mx; ++i) {
cntG[i] += cntG[i - 1];
}
vector<int> ans;
for (auto&& q : queries) {
ans.push_back(upper_bound(cntG.begin(), cntG.end(), q) - cntG.begin());
}
return ans;
}
};
func gcdValues(nums []int, queries []int64) (ans []int) {
mx := slices.Max(nums)
cnt := make([]int, mx+1)
cntG := make([]int, mx+1)
for _, x := range nums {
cnt[x]++
}
for i := mx; i > 0; i-- {
var v int
for j := i; j <= mx; j += i {
v += cnt[j]
cntG[i] -= cntG[j]
}
cntG[i] += v * (v - 1) / 2
}
for i := 2; i <= mx; i++ {
cntG[i] += cntG[i-1]
}
for _, q := range queries {
ans = append(ans, sort.SearchInts(cntG, int(q)+1))
}
return
}