@@ -112,32 +112,352 @@ tags:
112
112
113
113
<!-- solution:start -->
114
114
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}$。
116
150
117
151
<!-- tabs:start -->
118
152
119
153
#### Python3
120
154
121
155
``` 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]
123
203
```
124
204
125
205
#### Java
126
206
127
207
``` 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
+ }
129
287
```
130
288
131
289
#### C++
132
290
133
291
``` 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
+ };
135
381
```
136
382
137
383
#### Go
138
384
139
385
```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
+ }
141
461
```
142
462
143
463
<!-- tabs:end -->
0 commit comments