Skip to content

Commit a0d941e

Browse files
authored
feat: add solutions to lc problem: No.3312 (doocs#3606)
No.3312.Sorted GCD Pair Queries
1 parent 18de759 commit a0d941e

File tree

6 files changed

+340
-10
lines changed

6 files changed

+340
-10
lines changed

solution/3300-3399/3312.Sorted GCD Pair Queries/README.md

Lines changed: 118 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,32 +84,145 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3300-3399/3312.So
8484

8585
<!-- solution:start -->
8686

87-
### 方法一
87+
### 方法一:预处理 + 前缀和 + 二分查找
88+
89+
我们可以预处理得到数组 $\textit{nums}$ 中的所有数对的最大公约数的出现次数,记录在数组 $\textit{cntG}$ 中。然后,我们计算数组 $\textit{cntG}$ 的前缀和。最后,对于每个查询,我们可以通过二分查找在数组 $\textit{cntG}$ 中找到第一个大于 $\textit{queries}[i]$ 的元素的下标,即为答案。
90+
91+
我们用 $\textit{mx}$ 表示数组 $\textit{nums}$ 中的最大值,用 $\textit{cnt}$ 记录数组 $\textit{nums}$ 中每个数的出现次数。我们用 $\textit{cntG}[i]$ 表示数组 $\textit{nums}$ 中最大公约数等于 $i$ 的数对个数。为了计算 $\textit{cntG}[i]$,我们可以按照以下步骤进行:
92+
93+
1. 计算数组 $\textit{nums}$ 中 $i$ 的倍数的出现次数 $v$,那么从这些元素中任选两个元素组成的数对一定满足最大公约数是 $i$ 的倍数,即 $\textit{cntG}[i]$ 需要增加 $v \times (v - 1) / 2$;
94+
1. 我们需要排除最大公约数是 $i$ 的倍数且大于 $i$ 的数对,因此,对于 $i$ 的倍数 $j$,我们需要减去 $\textit{cntG}[j]$。
95+
96+
以上需要我们从大到小遍历 $i$,这样才能保证我们在计算 $\textit{cntG}[i]$ 时已经计算了所有的 $\textit{cntG}[j]$。
97+
98+
最后,我们计算数组 $\textit{cntG}$ 的前缀和,然后对于每个查询,我们可以通过二分查找在数组 $\textit{cntG}$ 中找到第一个大于 $\textit{queries}[i]$ 的元素的下标,即为答案。
99+
100+
时间复杂度 $O(n + (M + q) \times \log M)$,空间复杂度 $O(M)$。其中 $n$ 和 $M$ 分别是数组 $\textit{nums}$ 的长度和最大值,而 $q$ 是查询的数量。
88101

89102
<!-- tabs:start -->
90103

91104
#### Python3
92105

93106
```python
94-
107+
class Solution:
108+
def gcdValues(self, nums: List[int], queries: List[int]) -> List[int]:
109+
mx = max(nums)
110+
cnt = Counter(nums)
111+
cnt_g = [0] * (mx + 1)
112+
for i in range(mx, 0, -1):
113+
v = 0
114+
for j in range(i, mx + 1, i):
115+
v += cnt[j]
116+
cnt_g[i] -= cnt_g[j]
117+
cnt_g[i] += v * (v - 1) // 2
118+
s = list(accumulate(cnt_g))
119+
return [bisect_right(s, q) for q in queries]
95120
```
96121

97122
#### Java
98123

99124
```java
100-
125+
class Solution {
126+
public int[] gcdValues(int[] nums, long[] queries) {
127+
int mx = Arrays.stream(nums).max().getAsInt();
128+
int[] cnt = new int[mx + 1];
129+
long[] cntG = new long[mx + 1];
130+
for (int x : nums) {
131+
++cnt[x];
132+
}
133+
for (int i = mx; i > 0; --i) {
134+
int v = 0;
135+
for (int j = i; j <= mx; j += i) {
136+
v += cnt[j];
137+
cntG[i] -= cntG[j];
138+
}
139+
cntG[i] += 1L * v * (v - 1) / 2;
140+
}
141+
for (int i = 2; i <= mx; ++i) {
142+
cntG[i] += cntG[i - 1];
143+
}
144+
int m = queries.length;
145+
int[] ans = new int[m];
146+
for (int i = 0; i < m; ++i) {
147+
ans[i] = search(cntG, queries[i]);
148+
}
149+
return ans;
150+
}
151+
152+
private int search(long[] nums, long x) {
153+
int n = nums.length;
154+
int l = 0, r = n;
155+
while (l < r) {
156+
int mid = l + r >> 1;
157+
if (nums[mid] > x) {
158+
r = mid;
159+
} else {
160+
l = mid + 1;
161+
}
162+
}
163+
return l;
164+
}
165+
}
101166
```
102167

103168
#### C++
104169

105170
```cpp
106-
171+
class Solution {
172+
public:
173+
vector<int> gcdValues(vector<int>& nums, vector<long long>& queries) {
174+
int mx = ranges::max(nums);
175+
vector<int> cnt(mx + 1);
176+
vector<long long> cntG(mx + 1);
177+
for (int x : nums) {
178+
++cnt[x];
179+
}
180+
for (int i = mx; i; --i) {
181+
long long v = 0;
182+
for (int j = i; j <= mx; j += i) {
183+
v += cnt[j];
184+
cntG[i] -= cntG[j];
185+
}
186+
cntG[i] += 1LL * v * (v - 1) / 2;
187+
}
188+
for (int i = 2; i <= mx; ++i) {
189+
cntG[i] += cntG[i - 1];
190+
}
191+
vector<int> ans;
192+
for (auto&& q : queries) {
193+
ans.push_back(upper_bound(cntG.begin(), cntG.end(), q) - cntG.begin());
194+
}
195+
return ans;
196+
}
197+
};
107198
```
108199
109200
#### Go
110201
111202
```go
112-
203+
func gcdValues(nums []int, queries []int64) (ans []int) {
204+
mx := slices.Max(nums)
205+
cnt := make([]int, mx+1)
206+
cntG := make([]int, mx+1)
207+
for _, x := range nums {
208+
cnt[x]++
209+
}
210+
for i := mx; i > 0; i-- {
211+
var v int
212+
for j := i; j <= mx; j += i {
213+
v += cnt[j]
214+
cntG[i] -= cntG[j]
215+
}
216+
cntG[i] += v * (v - 1) / 2
217+
}
218+
for i := 2; i <= mx; i++ {
219+
cntG[i] += cntG[i-1]
220+
}
221+
for _, q := range queries {
222+
ans = append(ans, sort.SearchInts(cntG, int(q)+1))
223+
}
224+
return
225+
}
113226
```
114227

115228
<!-- tabs:end -->

solution/3300-3399/3312.Sorted GCD Pair Queries/README_EN.md

Lines changed: 118 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,32 +82,145 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3300-3399/3312.So
8282

8383
<!-- solution:start -->
8484

85-
### Solution 1
85+
### Solution 1: Preprocessing + Prefix Sum + Binary Search
86+
87+
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.
88+
89+
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:
90+
91+
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$;
92+
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]$.
93+
94+
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]$.
95+
96+
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.
97+
98+
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.
8699

87100
<!-- tabs:start -->
88101

89102
#### Python3
90103

91104
```python
92-
105+
class Solution:
106+
def gcdValues(self, nums: List[int], queries: List[int]) -> List[int]:
107+
mx = max(nums)
108+
cnt = Counter(nums)
109+
cnt_g = [0] * (mx + 1)
110+
for i in range(mx, 0, -1):
111+
v = 0
112+
for j in range(i, mx + 1, i):
113+
v += cnt[j]
114+
cnt_g[i] -= cnt_g[j]
115+
cnt_g[i] += v * (v - 1) // 2
116+
s = list(accumulate(cnt_g))
117+
return [bisect_right(s, q) for q in queries]
93118
```
94119

95120
#### Java
96121

97122
```java
98-
123+
class Solution {
124+
public int[] gcdValues(int[] nums, long[] queries) {
125+
int mx = Arrays.stream(nums).max().getAsInt();
126+
int[] cnt = new int[mx + 1];
127+
long[] cntG = new long[mx + 1];
128+
for (int x : nums) {
129+
++cnt[x];
130+
}
131+
for (int i = mx; i > 0; --i) {
132+
int v = 0;
133+
for (int j = i; j <= mx; j += i) {
134+
v += cnt[j];
135+
cntG[i] -= cntG[j];
136+
}
137+
cntG[i] += 1L * v * (v - 1) / 2;
138+
}
139+
for (int i = 2; i <= mx; ++i) {
140+
cntG[i] += cntG[i - 1];
141+
}
142+
int m = queries.length;
143+
int[] ans = new int[m];
144+
for (int i = 0; i < m; ++i) {
145+
ans[i] = search(cntG, queries[i]);
146+
}
147+
return ans;
148+
}
149+
150+
private int search(long[] nums, long x) {
151+
int n = nums.length;
152+
int l = 0, r = n;
153+
while (l < r) {
154+
int mid = l + r >> 1;
155+
if (nums[mid] > x) {
156+
r = mid;
157+
} else {
158+
l = mid + 1;
159+
}
160+
}
161+
return l;
162+
}
163+
}
99164
```
100165

101166
#### C++
102167

103168
```cpp
104-
169+
class Solution {
170+
public:
171+
vector<int> gcdValues(vector<int>& nums, vector<long long>& queries) {
172+
int mx = ranges::max(nums);
173+
vector<int> cnt(mx + 1);
174+
vector<long long> cntG(mx + 1);
175+
for (int x : nums) {
176+
++cnt[x];
177+
}
178+
for (int i = mx; i; --i) {
179+
long long v = 0;
180+
for (int j = i; j <= mx; j += i) {
181+
v += cnt[j];
182+
cntG[i] -= cntG[j];
183+
}
184+
cntG[i] += 1LL * v * (v - 1) / 2;
185+
}
186+
for (int i = 2; i <= mx; ++i) {
187+
cntG[i] += cntG[i - 1];
188+
}
189+
vector<int> ans;
190+
for (auto&& q : queries) {
191+
ans.push_back(upper_bound(cntG.begin(), cntG.end(), q) - cntG.begin());
192+
}
193+
return ans;
194+
}
195+
};
105196
```
106197
107198
#### Go
108199
109200
```go
110-
201+
func gcdValues(nums []int, queries []int64) (ans []int) {
202+
mx := slices.Max(nums)
203+
cnt := make([]int, mx+1)
204+
cntG := make([]int, mx+1)
205+
for _, x := range nums {
206+
cnt[x]++
207+
}
208+
for i := mx; i > 0; i-- {
209+
var v int
210+
for j := i; j <= mx; j += i {
211+
v += cnt[j]
212+
cntG[i] -= cntG[j]
213+
}
214+
cntG[i] += v * (v - 1) / 2
215+
}
216+
for i := 2; i <= mx; i++ {
217+
cntG[i] += cntG[i-1]
218+
}
219+
for _, q := range queries {
220+
ans = append(ans, sort.SearchInts(cntG, int(q)+1))
221+
}
222+
return
223+
}
111224
```
112225

113226
<!-- tabs:end -->
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
class Solution {
2+
public:
3+
vector<int> gcdValues(vector<int>& nums, vector<long long>& queries) {
4+
int mx = ranges::max(nums);
5+
vector<int> cnt(mx + 1);
6+
vector<long long> cntG(mx + 1);
7+
for (int x : nums) {
8+
++cnt[x];
9+
}
10+
for (int i = mx; i; --i) {
11+
long long v = 0;
12+
for (int j = i; j <= mx; j += i) {
13+
v += cnt[j];
14+
cntG[i] -= cntG[j];
15+
}
16+
cntG[i] += 1LL * v * (v - 1) / 2;
17+
}
18+
for (int i = 2; i <= mx; ++i) {
19+
cntG[i] += cntG[i - 1];
20+
}
21+
vector<int> ans;
22+
for (auto&& q : queries) {
23+
ans.push_back(upper_bound(cntG.begin(), cntG.end(), q) - cntG.begin());
24+
}
25+
return ans;
26+
}
27+
};
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
func gcdValues(nums []int, queries []int64) (ans []int) {
2+
mx := slices.Max(nums)
3+
cnt := make([]int, mx+1)
4+
cntG := make([]int, mx+1)
5+
for _, x := range nums {
6+
cnt[x]++
7+
}
8+
for i := mx; i > 0; i-- {
9+
var v int
10+
for j := i; j <= mx; j += i {
11+
v += cnt[j]
12+
cntG[i] -= cntG[j]
13+
}
14+
cntG[i] += v * (v - 1) / 2
15+
}
16+
for i := 2; i <= mx; i++ {
17+
cntG[i] += cntG[i-1]
18+
}
19+
for _, q := range queries {
20+
ans = append(ans, sort.SearchInts(cntG, int(q)+1))
21+
}
22+
return
23+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
class Solution {
2+
public int[] gcdValues(int[] nums, long[] queries) {
3+
int mx = Arrays.stream(nums).max().getAsInt();
4+
int[] cnt = new int[mx + 1];
5+
long[] cntG = new long[mx + 1];
6+
for (int x : nums) {
7+
++cnt[x];
8+
}
9+
for (int i = mx; i > 0; --i) {
10+
int v = 0;
11+
for (int j = i; j <= mx; j += i) {
12+
v += cnt[j];
13+
cntG[i] -= cntG[j];
14+
}
15+
cntG[i] += 1L * v * (v - 1) / 2;
16+
}
17+
for (int i = 2; i <= mx; ++i) {
18+
cntG[i] += cntG[i - 1];
19+
}
20+
int m = queries.length;
21+
int[] ans = new int[m];
22+
for (int i = 0; i < m; ++i) {
23+
ans[i] = search(cntG, queries[i]);
24+
}
25+
return ans;
26+
}
27+
28+
private int search(long[] nums, long x) {
29+
int n = nums.length;
30+
int l = 0, r = n;
31+
while (l < r) {
32+
int mid = l + r >> 1;
33+
if (nums[mid] > x) {
34+
r = mid;
35+
} else {
36+
l = mid + 1;
37+
}
38+
}
39+
return l;
40+
}
41+
}

0 commit comments

Comments
 (0)