Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 118 additions & 5 deletions solution/3300-3399/3312.Sorted GCD Pair Queries/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,32 +84,145 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3300-3399/3312.So

<!-- solution:start -->

### 方法一
### 方法一:预处理 + 前缀和 + 二分查找

我们可以预处理得到数组 $\textit{nums}$ 中的所有数对的最大公约数的出现次数,记录在数组 $\textit{cntG}$ 中。然后,我们计算数组 $\textit{cntG}$ 的前缀和。最后,对于每个查询,我们可以通过二分查找在数组 $\textit{cntG}$ 中找到第一个大于 $\textit{queries}[i]$ 的元素的下标,即为答案。

我们用 $\textit{mx}$ 表示数组 $\textit{nums}$ 中的最大值,用 $\textit{cnt}$ 记录数组 $\textit{nums}$ 中每个数的出现次数。我们用 $\textit{cntG}[i]$ 表示数组 $\textit{nums}$ 中最大公约数等于 $i$ 的数对个数。为了计算 $\textit{cntG}[i]$,我们可以按照以下步骤进行:

1. 计算数组 $\textit{nums}$ 中 $i$ 的倍数的出现次数 $v$,那么从这些元素中任选两个元素组成的数对一定满足最大公约数是 $i$ 的倍数,即 $\textit{cntG}[i]$ 需要增加 $v \times (v - 1) / 2$;
1. 我们需要排除最大公约数是 $i$ 的倍数且大于 $i$ 的数对,因此,对于 $i$ 的倍数 $j$,我们需要减去 $\textit{cntG}[j]$。

以上需要我们从大到小遍历 $i$,这样才能保证我们在计算 $\textit{cntG}[i]$ 时已经计算了所有的 $\textit{cntG}[j]$。

最后,我们计算数组 $\textit{cntG}$ 的前缀和,然后对于每个查询,我们可以通过二分查找在数组 $\textit{cntG}$ 中找到第一个大于 $\textit{queries}[i]$ 的元素的下标,即为答案。

时间复杂度 $O(n + (M + q) \times \log M)$,空间复杂度 $O(M)$。其中 $n$ 和 $M$ 分别是数组 $\textit{nums}$ 的长度和最大值,而 $q$ 是查询的数量。

<!-- tabs:start -->

#### Python3

```python

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]
```

#### Java

```java

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;
}
}
```

#### C++

```cpp

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;
}
};
```

#### Go

```go

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
}
```

<!-- tabs:end -->
Expand Down
123 changes: 118 additions & 5 deletions solution/3300-3399/3312.Sorted GCD Pair Queries/README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,32 +82,145 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3300-3399/3312.So

<!-- solution:start -->

### Solution 1
### Solution 1: Preprocessing + Prefix Sum + Binary Search

We can preprocess to obtain the occurrence count of the greatest common divisor (GCD) of all pairs in the array $\textit{nums}$, recorded in the array $\textit{cntG}$. Then, we calculate the prefix sum of the array $\textit{cntG}$. Finally, for each query, we can use binary search to find the index of the first element in the array $\textit{cntG}$ that is greater than $\textit{queries}[i]$, which is the answer.

Let $\textit{mx}$ denote the maximum value in the array $\textit{nums}$, and let $\textit{cnt}$ record the occurrence count of each number in the array $\textit{nums}$. Let $\textit{cntG}[i]$ denote the number of pairs in the array $\textit{nums}$ whose GCD is equal to $i$. To calculate $\textit{cntG}[i]$, we can follow these steps:

1. Calculate the occurrence count $v$ of multiples of $i$ in the array $\textit{nums}$. Then, the number of pairs formed by any two elements from these multiples must have a GCD that is a multiple of $i$, i.e., $\textit{cntG}[i]$ needs to be increased by $v \times (v - 1) / 2$;
2. We need to exclude pairs whose GCD is a multiple of $i$ and greater than $i$. Therefore, for multiples $j$ of $i$, we need to subtract $\textit{cntG}[j]$.

The above steps require us to traverse $i$ from large to small so that when calculating $\textit{cntG}[i]$, we have already calculated all $\textit{cntG}[j]$.

Finally, we calculate the prefix sum of the array $\textit{cntG}$, and for each query, we can use binary search to find the index of the first element in the array $\textit{cntG}$ that is greater than $\textit{queries}[i]$, which is the answer.

The time complexity is $O(n + (M + q) \times \log M)$, and the space complexity is $O(M)$. Here, $n$ and $M$ represent the length and the maximum value of the array $\textit{nums}$, respectively, and $q$ represents the number of queries.

<!-- tabs:start -->

#### Python3

```python

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]
```

#### Java

```java

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;
}
}
```

#### C++

```cpp

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;
}
};
```

#### Go

```go

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
}
```

<!-- tabs:end -->
Expand Down
27 changes: 27 additions & 0 deletions solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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;
}
};
23 changes: 23 additions & 0 deletions solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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
}
41 changes: 41 additions & 0 deletions solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
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;
}
}
Loading
Loading