From dd79d36518cbe52f483746fb710ff12308f4c0d3 Mon Sep 17 00:00:00 2001 From: yanglbme Date: Mon, 7 Oct 2024 15:15:32 +0800 Subject: [PATCH] feat: add solutions to lc problem: No.3312 No.3312.Sorted GCD Pair Queries --- .../3312.Sorted GCD Pair Queries/README.md | 123 +++++++++++++++++- .../3312.Sorted GCD Pair Queries/README_EN.md | 123 +++++++++++++++++- .../3312.Sorted GCD Pair Queries/Solution.cpp | 27 ++++ .../3312.Sorted GCD Pair Queries/Solution.go | 23 ++++ .../Solution.java | 41 ++++++ .../3312.Sorted GCD Pair Queries/Solution.py | 13 ++ 6 files changed, 340 insertions(+), 10 deletions(-) create mode 100644 solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.cpp create mode 100644 solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.go create mode 100644 solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.java create mode 100644 solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.py diff --git a/solution/3300-3399/3312.Sorted GCD Pair Queries/README.md b/solution/3300-3399/3312.Sorted GCD Pair Queries/README.md index 0eca79cd8200c..8ee6a639c1189 100644 --- a/solution/3300-3399/3312.Sorted GCD Pair Queries/README.md +++ b/solution/3300-3399/3312.Sorted GCD Pair Queries/README.md @@ -84,32 +84,145 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3300-3399/3312.So -### 方法一 +### 方法一:预处理 + 前缀和 + 二分查找 + +我们可以预处理得到数组 $\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$ 是查询的数量。 #### 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 gcdValues(vector& nums, vector& queries) { + int mx = ranges::max(nums); + vector cnt(mx + 1); + vector 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 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 +} ``` diff --git a/solution/3300-3399/3312.Sorted GCD Pair Queries/README_EN.md b/solution/3300-3399/3312.Sorted GCD Pair Queries/README_EN.md index 4fc570dc7ddd8..d7b607f44c0da 100644 --- a/solution/3300-3399/3312.Sorted GCD Pair Queries/README_EN.md +++ b/solution/3300-3399/3312.Sorted GCD Pair Queries/README_EN.md @@ -82,32 +82,145 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3300-3399/3312.So -### 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. #### 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 gcdValues(vector& nums, vector& queries) { + int mx = ranges::max(nums); + vector cnt(mx + 1); + vector 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 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 +} ``` diff --git a/solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.cpp b/solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.cpp new file mode 100644 index 0000000000000..047eeb6c8523e --- /dev/null +++ b/solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.cpp @@ -0,0 +1,27 @@ +class Solution { +public: + vector gcdValues(vector& nums, vector& queries) { + int mx = ranges::max(nums); + vector cnt(mx + 1); + vector 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 ans; + for (auto&& q : queries) { + ans.push_back(upper_bound(cntG.begin(), cntG.end(), q) - cntG.begin()); + } + return ans; + } +}; diff --git a/solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.go b/solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.go new file mode 100644 index 0000000000000..86e08bffb7451 --- /dev/null +++ b/solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.go @@ -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 +} diff --git a/solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.java b/solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.java new file mode 100644 index 0000000000000..9c05aedecb214 --- /dev/null +++ b/solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.java @@ -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; + } +} diff --git a/solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.py b/solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.py new file mode 100644 index 0000000000000..0097c34abb22a --- /dev/null +++ b/solution/3300-3399/3312.Sorted GCD Pair Queries/Solution.py @@ -0,0 +1,13 @@ +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]