Skip to content

Commit 7ee45c4

Browse files
authored
feat: add solutions to lc problem: No.3145 (doocs#3442)
No.3145.Find Products of Elements of Big Array
1 parent 6b87c24 commit 7ee45c4

File tree

6 files changed

+937
-10
lines changed

6 files changed

+937
-10
lines changed

solution/3100-3199/3145.Find Products of Elements of Big Array/README.md

Lines changed: 325 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,32 +112,352 @@ tags:
112112

113113
<!-- solution:start -->
114114

115-
### 方法一
115+
### 方法一:二分查找 + 位运算
116+
117+
连续的正整数数字对应的强整数数组连接得到数组 $\textit{big\_nums}$,题目需要我们求出对于每个查询 $[\textit{left}, \textit{right}, \textit{mod}]$,子数组 $\textit{big\_nums}[\textit{left}..\textit{right}]$ 的乘积对 $\textit{mod}$ 取模的结果。由于子数组每个元素都是 $2$ 的幂,这等价于求子数组的幂次之和 $\textit{power}$,然后计算 $2^{\textit{power}} \bmod \textit{mod}$。例如,对于子数组 $[1, 4, 8]$,即 $[2^0, 2^2, 2^3]$,其幂次之和为 $0 + 2 + 3 = 5$,所以 $2^5 \bmod \textit{mod}$ 就是我们要求的结果。
118+
119+
因此,我们不妨将 $\textit{big\_nums}$ 转换为幂次数组,即对于子数组 $[1, 4, 8]$,我们将其转换为 $[0, 2, 3]$。这样,问题转换为求幂次数组的子数组之和,即 $\textit{power} = \textit{f}(\textit{right} + 1) - \textit{f}(\textit{left})$,其中 $\textit{f}(i)$ 表示 $\textit{big\_nums}[0..i)$ 的幂次之和,也即是前缀和。
120+
121+
接下来,就是根据下标 $i$ 计算 $\textit{f}(i)$ 的值。我们可以使用二分查找的方法,先找到强数组长度和小于 $i$ 的最大数字,然后再计算剩下的数字的幂次之和。
122+
123+
我们根据题目描述,列出数字 $0..14$ 的强整数:
124+
125+
| $\textit{nums}$ | 8($2^3$) | 4($2^2$) | 2($2^1$) | ($2^0$) |
126+
| --------------- | ------------------------------------- | ------------------------------------- | ------------------------------------- | ------------------------------------- |
127+
| 0 | 0 | 0 | 0 | 0 |
128+
| 1 | <span style="color: red;">0</span> | <span style="color: red;">0</span> | <span style="color: red;">0</span> | <span style="color: red;">1</span> |
129+
| 2 | <span style="color: blue;">0</span> | <span style="color: blue;">0</span> | <span style="color: blue;">1</span> | <span style="color: blue;">0</span> |
130+
| 3 | <span style="color: blue;">0</span> | <span style="color: blue;">0</span> | <span style="color: blue;">1</span> | <span style="color: blue;">1</span> |
131+
| 4 | <span style="color: green;">0</span> | <span style="color: green;">1</span> | <span style="color: green;">0</span> | <span style="color: green;">0</span> |
132+
| 5 | <span style="color: green;">0</span> | <span style="color: green;">1</span> | <span style="color: green;">0</span> | <span style="color: green;">1</span> |
133+
| 6 | <span style="color: green;">0</span> | <span style="color: green;">1</span> | <span style="color: green;">1</span> | <span style="color: green;">0</span> |
134+
| 7 | <span style="color: green;">0</span> | <span style="color: green;">1</span> | <span style="color: green;">1</span> | <span style="color: green;">1</span> |
135+
| 8 | <span style="color: yellow;">1</span> | <span style="color: yellow;">0</span> | <span style="color: yellow;">0</span> | <span style="color: yellow;">0</span> |
136+
| 9 | <span style="color: yellow;">1</span> | <span style="color: yellow;">0</span> | <span style="color: yellow;">0</span> | <span style="color: yellow;">1</span> |
137+
| 10 | <span style="color: yellow;">1</span> | <span style="color: yellow;">0</span> | <span style="color: yellow;">1</span> | <span style="color: yellow;">0</span> |
138+
| 11 | <span style="color: yellow;">1</span> | <span style="color: yellow;">0</span> | <span style="color: yellow;">1</span> | <span style="color: yellow;">1</span> |
139+
| 12 | <span style="color: yellow;">1</span> | <span style="color: yellow;">1</span> | <span style="color: yellow;">0</span> | <span style="color: yellow;">0</span> |
140+
| 13 | <span style="color: yellow;">1</span> | <span style="color: yellow;">1</span> | <span style="color: yellow;">0</span> | <span style="color: yellow;">1</span> |
141+
| 14 | <span style="color: yellow;">1</span> | <span style="color: yellow;">1</span> | <span style="color: yellow;">1</span> | <span style="color: yellow;">0</span> |
142+
143+
将数字按照 $[2^i, 2^{i+1}-1]$ 的区间划分为不同的颜色,可以发现,区间 $[2^i, 2^{i+1}-1]$ 的数字,相当于在区间 $[0, 2^i-1]$ 的数字基础上,每个数字加上 $2^i$。我们可以根据这个规律,计算出 $\textit{big\_nums}$ 的前 $i$ 组的所有数字的强数组个数之和 $\textit{cnt}[i]$ 和幂次之和 $\textit{s}[i]$。
144+
145+
接下来,对于任何数字,我们考虑如何计算其强数组的个数和幂次之和。我们可以通过二进制的方式,从最高位开始,诸位计算。例如,对于数字 $13 = 2^3 + 2^2 + 2^0$,前 $2^3$ 个数字的结果可以由 $textit{cnt}[3]$ 和 $\textit{s}[3]$ 计算得到,而剩下的 $[2^3, 13]$ 的结果,相当于给 $[0, 13-2^3]$ 的所有数字,即 $[0, 5]$ 的所有数字的强数组增加 $3$,问题转换为计算 $[0, 5]$ 的所有数字的强数组的个数和幂次之和。这样,我们可以计算出任意数字的强数组的个数和幂次之和。
146+
147+
最后,我们可以根据 $\textit{power}$ 的值,利用快速幂的方法,计算出 $2^{\textit{power}} \bmod \textit{mod}$ 的结果。
148+
149+
时间复杂度 $O(q \times \log M)$,空间复杂度 $(\log M)$。其中 $q$ 为查询的个数,而 $M$ 为数字的上界,本题中 $M \le 10^{15}$。
116150

117151
<!-- tabs:start -->
118152

119153
#### Python3
120154

121155
```python
122-
156+
m = 50
157+
cnt = [0] * (m + 1)
158+
s = [0] * (m + 1)
159+
p = 1
160+
for i in range(1, m + 1):
161+
cnt[i] = cnt[i - 1] * 2 + p
162+
s[i] = s[i - 1] * 2 + p * (i - 1)
163+
p *= 2
164+
165+
166+
def num_idx_and_sum(x: int) -> tuple:
167+
idx = 0
168+
total_sum = 0
169+
while x:
170+
i = x.bit_length() - 1
171+
idx += cnt[i]
172+
total_sum += s[i]
173+
x -= 1 << i
174+
total_sum += (x + 1) * i
175+
idx += x + 1
176+
return (idx, total_sum)
177+
178+
179+
def f(i: int) -> int:
180+
l, r = 0, 1 << m
181+
while l < r:
182+
mid = (l + r + 1) >> 1
183+
idx, _ = num_idx_and_sum(mid)
184+
if idx < i:
185+
l = mid
186+
else:
187+
r = mid - 1
188+
189+
total_sum = 0
190+
idx, total_sum = num_idx_and_sum(l)
191+
i -= idx
192+
x = l + 1
193+
for _ in range(i):
194+
y = x & -x
195+
total_sum += y.bit_length() - 1
196+
x -= y
197+
return total_sum
198+
199+
200+
class Solution:
201+
def findProductsOfElements(self, queries: List[List[int]]) -> List[int]:
202+
return [pow(2, f(right + 1) - f(left), mod) for left, right, mod in queries]
123203
```
124204

125205
#### Java
126206

127207
```java
128-
208+
class Solution {
209+
private static final int M = 50;
210+
private static final long[] cnt = new long[M + 1];
211+
private static final long[] s = new long[M + 1];
212+
213+
static {
214+
long p = 1;
215+
for (int i = 1; i <= M; i++) {
216+
cnt[i] = cnt[i - 1] * 2 + p;
217+
s[i] = s[i - 1] * 2 + p * (i - 1);
218+
p *= 2;
219+
}
220+
}
221+
222+
private static long[] numIdxAndSum(long x) {
223+
long idx = 0;
224+
long totalSum = 0;
225+
while (x > 0) {
226+
int i = Long.SIZE - Long.numberOfLeadingZeros(x) - 1;
227+
idx += cnt[i];
228+
totalSum += s[i];
229+
x -= 1L << i;
230+
totalSum += (x + 1) * i;
231+
idx += x + 1;
232+
}
233+
return new long[] {idx, totalSum};
234+
}
235+
236+
private static long f(long i) {
237+
long l = 0;
238+
long r = 1L << M;
239+
while (l < r) {
240+
long mid = (l + r + 1) >> 1;
241+
long[] idxAndSum = numIdxAndSum(mid);
242+
long idx = idxAndSum[0];
243+
if (idx < i) {
244+
l = mid;
245+
} else {
246+
r = mid - 1;
247+
}
248+
}
249+
250+
long[] idxAndSum = numIdxAndSum(l);
251+
long totalSum = idxAndSum[1];
252+
long idx = idxAndSum[0];
253+
i -= idx;
254+
long x = l + 1;
255+
for (int j = 0; j < i; j++) {
256+
long y = x & -x;
257+
totalSum += Long.numberOfTrailingZeros(y);
258+
x -= y;
259+
}
260+
return totalSum;
261+
}
262+
263+
public int[] findProductsOfElements(long[][] queries) {
264+
int n = queries.length;
265+
int[] ans = new int[n];
266+
for (int i = 0; i < n; i++) {
267+
long left = queries[i][0];
268+
long right = queries[i][1];
269+
long mod = queries[i][2];
270+
long power = f(right + 1) - f(left);
271+
ans[i] = qpow(2, power, mod);
272+
}
273+
return ans;
274+
}
275+
276+
private int qpow(long a, long n, long mod) {
277+
long ans = 1 % mod;
278+
for (; n > 0; n >>= 1) {
279+
if ((n & 1) == 1) {
280+
ans = ans * a % mod;
281+
}
282+
a = a * a % mod;
283+
}
284+
return (int) ans;
285+
}
286+
}
129287
```
130288

131289
#### C++
132290

133291
```cpp
134-
292+
using ll = long long;
293+
const int m = 50;
294+
ll cnt[m + 1];
295+
ll s[m + 1];
296+
ll p = 1;
297+
298+
auto init = [] {
299+
cnt[0] = 0;
300+
s[0] = 0;
301+
for (int i = 1; i <= m; ++i) {
302+
cnt[i] = cnt[i - 1] * 2 + p;
303+
s[i] = s[i - 1] * 2 + p * (i - 1);
304+
p *= 2;
305+
}
306+
return 0;
307+
}();
308+
309+
pair<ll, ll> numIdxAndSum(ll x) {
310+
ll idx = 0;
311+
ll totalSum = 0;
312+
while (x > 0) {
313+
int i = 63 - __builtin_clzll(x);
314+
idx += cnt[i];
315+
totalSum += s[i];
316+
x -= 1LL << i;
317+
totalSum += (x + 1) * i;
318+
idx += x + 1;
319+
}
320+
return make_pair(idx, totalSum);
321+
}
322+
323+
ll f(ll i) {
324+
ll l = 0;
325+
ll r = 1LL << m;
326+
while (l < r) {
327+
ll mid = (l + r + 1) >> 1;
328+
auto idxAndSum = numIdxAndSum(mid);
329+
ll idx = idxAndSum.first;
330+
if (idx < i) {
331+
l = mid;
332+
} else {
333+
r = mid - 1;
334+
}
335+
}
336+
337+
auto idxAndSum = numIdxAndSum(l);
338+
ll totalSum = idxAndSum.second;
339+
ll idx = idxAndSum.first;
340+
i -= idx;
341+
ll x = l + 1;
342+
for (int j = 0; j < i; ++j) {
343+
ll y = x & -x;
344+
totalSum += __builtin_ctzll(y);
345+
x -= y;
346+
}
347+
return totalSum;
348+
}
349+
350+
ll qpow(ll a, ll n, ll mod) {
351+
ll ans = 1 % mod;
352+
a = a % mod;
353+
while (n > 0) {
354+
if (n & 1) {
355+
ans = ans * a % mod;
356+
}
357+
a = a * a % mod;
358+
n >>= 1;
359+
}
360+
return ans;
361+
}
362+
363+
class Solution {
364+
public:
365+
vector<int> findProductsOfElements(vector<vector<ll>>& queries) {
366+
int n = queries.size();
367+
vector<int> ans(n);
368+
for (int i = 0; i < n; ++i) {
369+
ll left = queries[i][0];
370+
ll right = queries[i][1];
371+
ll mod = queries[i][2];
372+
ll power = f(right + 1) - f(left);
373+
if (power < 0) {
374+
power += mod;
375+
}
376+
ans[i] = static_cast<int>(qpow(2, power, mod));
377+
}
378+
return ans;
379+
}
380+
};
135381
```
136382
137383
#### Go
138384
139385
```go
140-
386+
const m = 50
387+
388+
var cnt [m + 1]int64
389+
var s [m + 1]int64
390+
var p int64 = 1
391+
392+
func init() {
393+
cnt[0] = 0
394+
s[0] = 0
395+
for i := 1; i <= m; i++ {
396+
cnt[i] = cnt[i-1]*2 + p
397+
s[i] = s[i-1]*2 + p*(int64(i)-1)
398+
p *= 2
399+
}
400+
}
401+
402+
func numIdxAndSum(x int64) (int64, int64) {
403+
var idx, totalSum int64
404+
for x > 0 {
405+
i := 63 - bits.LeadingZeros64(uint64(x))
406+
idx += cnt[i]
407+
totalSum += s[i]
408+
x -= 1 << i
409+
totalSum += (x + 1) * int64(i)
410+
idx += x + 1
411+
}
412+
return idx, totalSum
413+
}
414+
415+
func f(i int64) int64 {
416+
l, r := int64(0), int64(1)<<m
417+
for l < r {
418+
mid := (l + r + 1) >> 1
419+
idx, _ := numIdxAndSum(mid)
420+
if idx < i {
421+
l = mid
422+
} else {
423+
r = mid - 1
424+
}
425+
}
426+
427+
_, totalSum := numIdxAndSum(l)
428+
idx, _ := numIdxAndSum(l)
429+
i -= idx
430+
x := l + 1
431+
for j := int64(0); j < i; j++ {
432+
y := x & -x
433+
totalSum += int64(bits.TrailingZeros64(uint64(y)))
434+
x -= y
435+
}
436+
return totalSum
437+
}
438+
439+
func qpow(a, n, mod int64) int64 {
440+
ans := int64(1) % mod
441+
a = a % mod
442+
for n > 0 {
443+
if n&1 == 1 {
444+
ans = (ans * a) % mod
445+
}
446+
a = (a * a) % mod
447+
n >>= 1
448+
}
449+
return ans
450+
}
451+
452+
func findProductsOfElements(queries [][]int64) []int {
453+
ans := make([]int, len(queries))
454+
for i, q := range queries {
455+
left, right, mod := q[0], q[1], q[2]
456+
power := f(right+1) - f(left)
457+
ans[i] = int(qpow(2, power, mod))
458+
}
459+
return ans
460+
}
141461
```
142462

143463
<!-- tabs:end -->

0 commit comments

Comments
 (0)