题目描述
一个非负整数 x
的 强数组 指的是满足元素为 2 的幂且元素总和为 x
的最短有序数组。下表说明了如何确定 强数组 的示例。可以证明,x
对应的强数组是独一无二的。
数字 |
二进制表示 |
强数组 |
1 |
00001 |
[1] |
8 |
01000 |
[8] |
10 |
01010 |
[2, 8] |
13 |
01101 |
[1, 4, 8] |
23 |
10111 |
[1, 2, 4, 16] |
我们将每一个升序的正整数 i
(即1,2,3等等)的 强数组 连接得到数组 big_nums
,big_nums
开始部分为 [1, 2, 1, 2, 4, 1, 4, 2, 4, 1, 2, 4, 8, ...]
。
给你一个二维整数数组 queries
,其中 queries[i] = [fromi, toi, modi]
,你需要计算 (big_nums[fromi] * big_nums[fromi + 1] * ... * big_nums[toi]) % modi
。
请你返回一个整数数组 answer
,其中 answer[i]
是第 i
个查询的答案。
示例 1:
输入:queries = [[1,3,7]]
输出:[4]
解释:
只有一个查询。
big_nums[1..3] = [2,1,2]
。它们的乘积为 4。结果为 4 % 7 = 4
。
示例 2:
输入:queries = [[2,5,3],[7,7,4]]
输出:[2,2]
解释:
有两个查询。
第一个查询:big_nums[2..5] = [1,2,4,1]
。它们的乘积为 8 。结果为 8 % 3 = 2
。
第二个查询:big_nums[7] = 2
。结果为 2 % 4 = 2
。
提示:
1 <= queries.length <= 500
queries[i].length == 3
0 <= queries[i][0] <= queries[i][1] <= 1015
1 <= queries[i][2] <= 105
解法
方法一:二分查找 + 位运算
连续的正整数数字对应的强整数数组连接得到数组 $\textit{bignums}$,题目需要我们求出对于每个查询 $[\textit{left}, \textit{right}, \textit{mod}]$,子数组 $\textit{bignums}[\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}$ 就是我们要求的结果。
因此,我们不妨将 $\textit{bignums}$ 转换为幂次数组,即对于子数组 $[1, 4, 8]$,我们将其转换为 $[0, 2, 3]$。这样,问题转换为求幂次数组的子数组之和,即 $\textit{power} = \textit{f}(\textit{right} + 1) - \textit{f}(\textit{left})$,其中 $\textit{f}(i)$ 表示 $\textit{bignums}[0..i)$ 的幂次之和,也即是前缀和。
接下来,就是根据下标 $i$ 计算 $\textit{f}(i)$ 的值。我们可以使用二分查找的方法,先找到强数组长度和小于 $i$ 的最大数字,然后再计算剩下的数字的幂次之和。
我们根据题目描述,列出数字 $0..14$ 的强整数:
$\textit{nums}$ |
8($2^3$) |
4($2^2$) |
2($2^1$) |
($2^0$) |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
1 |
2 |
0 |
0 |
1 |
0 |
3 |
0 |
0 |
1 |
1 |
4 |
0 |
1 |
0 |
0 |
5 |
0 |
1 |
0 |
1 |
6 |
0 |
1 |
1 |
0 |
7 |
0 |
1 |
1 |
1 |
8 |
1 |
0 |
0 |
0 |
9 |
1 |
0 |
0 |
1 |
10 |
1 |
0 |
1 |
0 |
11 |
1 |
0 |
1 |
1 |
12 |
1 |
1 |
0 |
0 |
13 |
1 |
1 |
0 |
1 |
14 |
1 |
1 |
1 |
0 |
将数字按照 $[2^i, 2^{i+1}-1]$ 的区间划分为不同的颜色,可以发现,区间 $[2^i, 2^{i+1}-1]$ 的数字,相当于在区间 $[0, 2^i-1]$ 的数字基础上,每个数字加上 $2^i$。我们可以根据这个规律,计算出 $\textit{bignums}$ 的前 $i$ 组的所有数字的强数组个数之和 $\textit{cnt}[i]$ 和幂次之和 $\textit{s}[i]$。
接下来,对于任何数字,我们考虑如何计算其强数组的个数和幂次之和。我们可以通过二进制的方式,从最高位开始,诸位计算。例如,对于数字 $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]$ 的所有数字的强数组的个数和幂次之和。这样,我们可以计算出任意数字的强数组的个数和幂次之和。
最后,我们可以根据 $\textit{power}$ 的值,利用快速幂的方法,计算出 $2^{\textit{power}} \bmod \textit{mod}$ 的结果。
时间复杂度 $O(q \times \log M)$,空间复杂度 $(\log M)$。其中 $q$ 为查询的个数,而 $M$ 为数字的上界,本题中 $M \le 10^{15}$。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 | m = 50
cnt = [0] * (m + 1)
s = [0] * (m + 1)
p = 1
for i in range(1, m + 1):
cnt[i] = cnt[i - 1] * 2 + p
s[i] = s[i - 1] * 2 + p * (i - 1)
p *= 2
def num_idx_and_sum(x: int) -> tuple:
idx = 0
total_sum = 0
while x:
i = x.bit_length() - 1
idx += cnt[i]
total_sum += s[i]
x -= 1 << i
total_sum += (x + 1) * i
idx += x + 1
return (idx, total_sum)
def f(i: int) -> int:
l, r = 0, 1 << m
while l < r:
mid = (l + r + 1) >> 1
idx, _ = num_idx_and_sum(mid)
if idx < i:
l = mid
else:
r = mid - 1
total_sum = 0
idx, total_sum = num_idx_and_sum(l)
i -= idx
x = l + 1
for _ in range(i):
y = x & -x
total_sum += y.bit_length() - 1
x -= y
return total_sum
class Solution:
def findProductsOfElements(self, queries: List[List[int]]) -> List[int]:
return [pow(2, f(right + 1) - f(left), mod) for left, right, mod in queries]
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 | class Solution {
private static final int M = 50;
private static final long[] cnt = new long[M + 1];
private static final long[] s = new long[M + 1];
static {
long p = 1;
for (int i = 1; i <= M; i++) {
cnt[i] = cnt[i - 1] * 2 + p;
s[i] = s[i - 1] * 2 + p * (i - 1);
p *= 2;
}
}
private static long[] numIdxAndSum(long x) {
long idx = 0;
long totalSum = 0;
while (x > 0) {
int i = Long.SIZE - Long.numberOfLeadingZeros(x) - 1;
idx += cnt[i];
totalSum += s[i];
x -= 1L << i;
totalSum += (x + 1) * i;
idx += x + 1;
}
return new long[] {idx, totalSum};
}
private static long f(long i) {
long l = 0;
long r = 1L << M;
while (l < r) {
long mid = (l + r + 1) >> 1;
long[] idxAndSum = numIdxAndSum(mid);
long idx = idxAndSum[0];
if (idx < i) {
l = mid;
} else {
r = mid - 1;
}
}
long[] idxAndSum = numIdxAndSum(l);
long totalSum = idxAndSum[1];
long idx = idxAndSum[0];
i -= idx;
long x = l + 1;
for (int j = 0; j < i; j++) {
long y = x & -x;
totalSum += Long.numberOfTrailingZeros(y);
x -= y;
}
return totalSum;
}
public int[] findProductsOfElements(long[][] queries) {
int n = queries.length;
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
long left = queries[i][0];
long right = queries[i][1];
long mod = queries[i][2];
long power = f(right + 1) - f(left);
ans[i] = qpow(2, power, mod);
}
return ans;
}
private int qpow(long a, long n, long mod) {
long ans = 1 % mod;
for (; n > 0; n >>= 1) {
if ((n & 1) == 1) {
ans = ans * a % mod;
}
a = a * a % mod;
}
return (int) ans;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 | using ll = long long;
const int m = 50;
ll cnt[m + 1];
ll s[m + 1];
ll p = 1;
auto init = [] {
cnt[0] = 0;
s[0] = 0;
for (int i = 1; i <= m; ++i) {
cnt[i] = cnt[i - 1] * 2 + p;
s[i] = s[i - 1] * 2 + p * (i - 1);
p *= 2;
}
return 0;
}();
pair<ll, ll> numIdxAndSum(ll x) {
ll idx = 0;
ll totalSum = 0;
while (x > 0) {
int i = 63 - __builtin_clzll(x);
idx += cnt[i];
totalSum += s[i];
x -= 1LL << i;
totalSum += (x + 1) * i;
idx += x + 1;
}
return make_pair(idx, totalSum);
}
ll f(ll i) {
ll l = 0;
ll r = 1LL << m;
while (l < r) {
ll mid = (l + r + 1) >> 1;
auto idxAndSum = numIdxAndSum(mid);
ll idx = idxAndSum.first;
if (idx < i) {
l = mid;
} else {
r = mid - 1;
}
}
auto idxAndSum = numIdxAndSum(l);
ll totalSum = idxAndSum.second;
ll idx = idxAndSum.first;
i -= idx;
ll x = l + 1;
for (int j = 0; j < i; ++j) {
ll y = x & -x;
totalSum += __builtin_ctzll(y);
x -= y;
}
return totalSum;
}
ll qpow(ll a, ll n, ll mod) {
ll ans = 1 % mod;
a = a % mod;
while (n > 0) {
if (n & 1) {
ans = ans * a % mod;
}
a = a * a % mod;
n >>= 1;
}
return ans;
}
class Solution {
public:
vector<int> findProductsOfElements(vector<vector<ll>>& queries) {
int n = queries.size();
vector<int> ans(n);
for (int i = 0; i < n; ++i) {
ll left = queries[i][0];
ll right = queries[i][1];
ll mod = queries[i][2];
ll power = f(right + 1) - f(left);
if (power < 0) {
power += mod;
}
ans[i] = static_cast<int>(qpow(2, power, mod));
}
return ans;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 | const m = 50
var cnt [m + 1]int64
var s [m + 1]int64
var p int64 = 1
func init() {
cnt[0] = 0
s[0] = 0
for i := 1; i <= m; i++ {
cnt[i] = cnt[i-1]*2 + p
s[i] = s[i-1]*2 + p*(int64(i)-1)
p *= 2
}
}
func numIdxAndSum(x int64) (int64, int64) {
var idx, totalSum int64
for x > 0 {
i := 63 - bits.LeadingZeros64(uint64(x))
idx += cnt[i]
totalSum += s[i]
x -= 1 << i
totalSum += (x + 1) * int64(i)
idx += x + 1
}
return idx, totalSum
}
func f(i int64) int64 {
l, r := int64(0), int64(1)<<m
for l < r {
mid := (l + r + 1) >> 1
idx, _ := numIdxAndSum(mid)
if idx < i {
l = mid
} else {
r = mid - 1
}
}
_, totalSum := numIdxAndSum(l)
idx, _ := numIdxAndSum(l)
i -= idx
x := l + 1
for j := int64(0); j < i; j++ {
y := x & -x
totalSum += int64(bits.TrailingZeros64(uint64(y)))
x -= y
}
return totalSum
}
func qpow(a, n, mod int64) int64 {
ans := int64(1) % mod
a = a % mod
for n > 0 {
if n&1 == 1 {
ans = (ans * a) % mod
}
a = (a * a) % mod
n >>= 1
}
return ans
}
func findProductsOfElements(queries [][]int64) []int {
ans := make([]int, len(queries))
for i, q := range queries {
left, right, mod := q[0], q[1], q[2]
power := f(right+1) - f(left)
ans[i] = int(qpow(2, power, mod))
}
return ans
}
|