3904.最小稳定下标II

目标

给你一个长度为 n 的整数数组 nums 和一个整数 k。

对于每个下标 i,定义它的 不稳定值 为 max(nums[0..i]) - min(nums[i..n - 1])。

换句话说:

  • max(nums[0..i]) 表示从下标 0 到下标 i 的元素中的 最大值 。
  • min(nums[i..n - 1]) 表示从下标 i 到下标 n - 1 的元素中的 最小值 。

如果某个下标 i 的不稳定值 小于等于 k,则称该下标为 稳定下标 。

返回 最小 的稳定下标。如果不存在这样的下标,则返回 -1。

示例 1:

输入: nums = [5,0,1,4], k = 3
输出: 3
解释:
在下标 0 处:[5] 中的最大值是 5,[5, 0, 1, 4] 中的最小值是 0,因此不稳定值为 5 - 0 = 5。
在下标 1 处:[5, 0] 中的最大值是 5,[0, 1, 4] 中的最小值是 0,因此不稳定值为 5 - 0 = 5。
在下标 2 处:[5, 0, 1] 中的最大值是 5,[1, 4] 中的最小值是 1,因此不稳定值为 5 - 1 = 4。
在下标 3 处:[5, 0, 1, 4] 中的最大值是 5,[4] 中的最小值是 4,因此不稳定值为 5 - 4 = 1。
这是第一个不稳定值小于等于 k = 3 的下标,因此答案是 3。

示例 2:

输入: nums = [3,2,1], k = 1
输出: -1
解释:
在下标 0 处,不稳定值为 3 - 1 = 2。
在下标 1 处,不稳定值为 3 - 1 = 2。
在下标 2 处,不稳定值为 3 - 1 = 2。
这些值都不小于等于 k = 1,因此答案是 -1。

示例 3:

输入: nums = [0], k = 0
输出: 0
解释:
在下标 0 处,不稳定值为 0 - 0 = 0,它小于等于 k = 0。因此答案是 0。

说明:

  • 1 <= nums.length <= 10^5
  • 0 <= nums[i] <= 10^9
  • 0 <= k <= 10^9

思路

3903.最小稳定下标I 相比数据范围扩大了。

代码


/**
 * @date 2026-09-07 9:01
 */
public class FirstStableIndex3904 {

    public int firstStableIndex(int[] nums, int k) {
        int n = nums.length;
        int[] suffix = new int[n + 1];
        Arrays.fill(suffix, Integer.MAX_VALUE);
        for (int i = n - 1; i >= 0; i--) {
            suffix[i] = Math.min(suffix[i + 1], nums[i]);
        }
        int max = 0;
        for (int i = 0; i < n; i++) {
            max = Math.max(max, nums[i]);
            if (max - suffix[i] <= k) {
                return i;
            }
        }
        return -1;
    }
}

性能

3903.最小稳定下标I

目标

给你一个长度为 n 的整数数组 nums 和一个整数 k。

对于每个下标 i,定义它的 不稳定值 为 max(nums[0..i]) - min(nums[i..n - 1])。

换句话说:

  • max(nums[0..i]) 表示从下标 0 到下标 i 的元素中的 最大值 。
  • min(nums[i..n - 1]) 表示从下标 i 到下标 n - 1 的元素中的 最小值 。

如果某个下标 i 的不稳定值 小于等于 k,则称该下标为 稳定下标 。

返回 最小 的稳定下标。如果不存在这样的下标,则返回 -1。

示例 1:

输入: nums = [5,0,1,4], k = 3
输出: 3
解释:
在下标 0 处:[5] 中的最大值是 5,[5, 0, 1, 4] 中的最小值是 0,因此不稳定值为 5 - 0 = 5。
在下标 1 处:[5, 0] 中的最大值是 5,[0, 1, 4] 中的最小值是 0,因此不稳定值为 5 - 0 = 5。
在下标 2 处:[5, 0, 1] 中的最大值是 5,[1, 4] 中的最小值是 1,因此不稳定值为 5 - 1 = 4。
在下标 3 处:[5, 0, 1, 4] 中的最大值是 5,[4] 中的最小值是 4,因此不稳定值为 5 - 4 = 1。
这是第一个不稳定值小于等于 k = 3 的下标,因此答案是 3。

示例 2:

输入: nums = [3,2,1], k = 1
输出: -1
解释:
在下标 0 处,不稳定值为 3 - 1 = 2。
在下标 1 处,不稳定值为 3 - 1 = 2。
在下标 2 处,不稳定值为 3 - 1 = 2。
这些值都不小于等于 k = 1,因此答案是 -1。

示例 3:

输入: nums = [0], k = 0
输出: 0
解释:
在下标 0 处,不稳定值为 0 - 0 = 0,它小于等于 k = 0。因此答案是 0。

说明:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 10^9
  • 0 <= k <= 10^9

思路

定义下标 i 的不稳定值为 max(nums[0..i]) - min(nums[i..n - 1]),如果该值小于等于 k 则称 i 为稳定下标。返回最小的稳定下标。

前后缀分解,计算后缀最小值,从左到右枚举,记录前缀最大值,如果满足条件直接返回,否则返回 -1

代码


/**
 * @date 2026-09-04 9:14
 */
public class FirstStableIndex3903 {

    public int firstStableIndex(int[] nums, int k) {
        int n = nums.length;
        int[] suffix = new int[n + 1];
        Arrays.fill(suffix, Integer.MAX_VALUE);
        for (int i = n - 1; i >= 0; i--) {
            suffix[i] = Math.min(suffix[i + 1], nums[i]);
        }
        int max = 0;
        for (int i = 0; i < n; i++) {
            max = Math.max(max, nums[i]);
            if (max - suffix[i] <= k) {
                return i;
            }
        }
        return -1;
    }

}

性能

3756.连接非零数字并乘以其数字和II

目标

给你一个长度为 m 的字符串 s,其中仅包含数字。另给你一个二维整数数组 queries,其中 queries[i] = [li, ri]。

对于每个 queries[i],提取 子串 s[li..ri],然后执行以下操作:

  • 将子串中所有 非零数字 按照原始顺序连接起来,形成一个新的整数 x。如果没有非零数字,则 x = 0。
  • 令 sum 为 x 中所有数字的 数字和 。答案为 x * sum。

返回一个整数数组 answer,其中 answer[i] 是第 i 个查询的答案。

由于答案可能非常大,请返回其对 10^9 + 7 取余数的结果。

子串 是字符串中的一个连续、非空 字符序列。

示例 1:

输入: s = "10203004", queries = [[0,7],[1,3],[4,6]]
输出: [12340, 4, 9]
解释:
s[0..7] = "10203004"
    x = 1234
    sum = 1 + 2 + 3 + 4 = 10
    因此,答案是 1234 * 10 = 12340。
s[1..3] = "020"
    x = 2
    sum = 2
    因此,答案是 2 * 2 = 4。
s[4..6] = "300"
    x = 3
    sum = 3
    因此,答案是 3 * 3 = 9。

示例 2:

输入: s = "1000", queries = [[0,3],[1,1]]
输出: [1, 0]
解释:
s[0..3] = "1000"
    x = 1
    sum = 1
    因此,答案是 1 * 1 = 1。
s[1..1] = "0"
    x = 0
    sum = 0
    因此,答案是 0 * 0 = 0。

示例 3:

输入: s = "9876543210", queries = [[0,9]]
输出: [444444137]
解释:
s[0..9] = "9876543210"
    x = 987654321
    sum = 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 = 45
    因此,答案是 987654321 * 45 = 44444444445。
    返回结果为 44444444445 mod (10^9 + 7) = 444444137。

说明:

  • 1 <= m == s.length <= 10^5
  • s 仅由数字组成。
  • 1 <= queries.length <= 10^5
  • queries[i] = [li, ri]
  • 0 <= li <= ri < m

思路

有一个数字字符串 s,针对每一个子串 s[queries[i][0], queries[i][1]],返回其非零数字所表示的数字 乘以 每位数字之和 对 1000000007 取余的结果。

3754.连接非零数字并乘以其数字和I 相比,本题的数字是由 queries 给出的子串,需要返回每一个子串的结果。

数位和可以使用前缀和快速计算。子串非零数字所表示的数字也可以通过前缀计算。

区间 [l, r] 所表示的数字对 MOD 取模的值为 (prefixNum[r + 1] + MOD - prefixNum[l] * base[k] % MOD) % MOD,例如,1230456[2, 4] 中的非零数字所表示的数字是 34,它等于 prefixNum[5]:1234 - prefixNum[2]:12 * 100,其中 100 = 10^kk 表示 [l, r] 中非零数字的个数。

代码


/**
 * @date 2026-07-08 9:50
 */
public class SumAndMultiply3756 {

    public int[] sumAndMultiply(String s, int[][] queries) {
        int n = s.length();
        int[] prefix = new int[n + 1];
        int[] prefixLength = new int[n + 1];
        long[] prefixNum = new long[n + 1];
        for (int i = 0; i < n; i++) {
            int d = s.charAt(i) - '0';
            prefix[i + 1] = prefix[i] + d;
            prefixLength[i + 1] = prefixLength[i] + (d != 0 ? 1 : 0);
            prefixNum[i + 1] = (prefixNum[i] * (d != 0 ? 10 : 1) + d) % MOD;
        }
        int ql = queries.length;
        int[] res = new int[ql];
        for (int i = 0; i < ql; i++) {
            int l = queries[i][0];
            int r = queries[i][1];
            int sum = prefix[r + 1] - prefix[l];
            long x = (prefixNum[r + 1] + MOD - prefixNum[l] * base[prefixLength[r + 1] - prefixLength[l]] % MOD) % MOD;
            res[i] = (int) (x * sum % MOD);
        }
        return res;
    }

}

性能

1358.包含所有三种字符的子字符串数目

目标

给你一个字符串 s ,它只包含三种字符 a, b 和 c 。

请你返回 a,b 和 c 都 至少 出现过一次的子字符串数目。

示例 1:

输入:s = "abcabc"
输出:10
解释:包含 a,b 和 c 各至少一次的子字符串为 "abc", "abca", "abcab", "abcabc", "bca", "bcab", "bcabc", "cab", "cabc" 和 "abc" (相同字符串算多次)。

示例 2:

输入:s = "aaacb"
输出:3
解释:包含 a,b 和 c 各至少一次的子字符串为 "aaacb", "aacb" 和 "acb" 。

示例 3:

输入:s = "abc"
输出:1

说明:

  • 3 <= s.length <= 5 x 10^4
  • s 只包含字符 a,b 和 c 。

思路

有一个字符串只包含三个字符 a b c,求这三个字符至少出现一次的子串个数。

直接的想法是计算这三个字符出现次数的前缀和,然后使用滑动窗口。使用前缀和来判断窗口内的子串是否满足条件,如果 [l, r] 满足条件,那么 [l, r ~ n - 1] 也满足条件。

判断字符是否至少出现一次可以不用前缀和,使用三个变量记录它们上次出现的下标,如果小于左端点则用 -1 标记。

代码


/**
 * @date 2026-06-30 9:14
 */
public class NumberOfSubstrings1358 {

    public int numberOfSubstrings(String s) {
        int n = s.length();
        int a = -1, b = -1, c = -1;
        int l = 0;
        int res = 0;
        for (int i = 0; i < n; i++) {
            if (s.charAt(i) == 'a') {
                a = i;
            } else if (s.charAt(i) == 'b') {
                b = i;
            } else {
                c = i;
            }
            while (a >= 0 && b >= 0 && c >= 0) {
                res += n - i;
                if (a <= l) {
                    a = -1;
                } else if (b <= l) {
                    b = -1;
                } else if (c <= l) {
                    c = -1;
                }
                l++;
            }
        }
        return res;
    }

}

性能

3737.统计主要元素子数组数目I

目标

给你一个整数数组 nums 和一个整数 target。

返回数组 nums 中满足 target 是 主要元素 的 子数组 的数目。

一个子数组的 主要元素 是指该元素在该子数组中出现的次数 严格大于 其长度的 一半 。

子数组 是数组中的一段连续且 非空 的元素序列。

示例 1:

输入: nums = [1,2,2,3], target = 2
输出: 5
解释:
以 target = 2 为主要元素的子数组有:
nums[1..1] = [2]
nums[2..2] = [2]
nums[1..2] = [2,2]
nums[0..2] = [1,2,2]
nums[1..3] = [2,2,3]
因此共有 5 个这样的子数组。

示例 2:

输入: nums = [1,1,1,1], target = 1
输出: 10
解释:
所有 10 个子数组都以 1 为主要元素。

示例 3:

输入: nums = [1,2,3], target = 4
输出: 0
解释:
target = 4 完全没有出现在 nums 中。因此,不可能有任何以 4 为主要元素的子数组。故答案为 0。

说明:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i] <= 10^9
  • 1 <= target <= 10^9

思路

定义子数组的 主要元素 为出现次数 严格大于 子数组长度一半 的元素。有一个数组 nums,找出以 target 为主要元素的子数组个数。

使用前缀和记录 target 的出现次数,保留循环子数组,判断 target 是否是它的主要元素即可。

代码


/**
 * @date 2026-06-25 9:12
 */
public class CountMajoritySubarrays3737 {

    public int countMajoritySubarrays(int[] nums, int target) {
        int n = nums.length;
        int[] prefix = new int[n + 1];
        for (int i = 0; i < n; i++) {
            prefix[i + 1] = prefix[i] + (nums[i] == target ? 1 : 0);
        }
        int res = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i; j < n; j++) {
                int l = j - i + 1;
                if (prefix[j + 1] - prefix[i] > l / 2) {
                    res++;
                }
            }
        }
        return res;
    }

}

性能

1732.找到最高海拔

目标

有一个自行车手打算进行一场公路骑行,这条路线总共由 n + 1 个不同海拔的点组成。自行车手从海拔为 0 的点 0 开始骑行。

给你一个长度为 n 的整数数组 gain ,其中 gain[i] 是点 i 和点 i + 1 的 净海拔高度差(0 <= i < n)。请你返回 最高点的海拔 。

示例 1:

输入:gain = [-5,1,5,0,-7]
输出:1
解释:海拔高度依次为 [0,-5,-4,1,1,-6] 。最高海拔为 1 。

示例 2:

输入:gain = [-4,-3,-2,-1,4,3,2]
输出:0
解释:海拔高度依次为 [0,-4,-7,-9,-10,-6,-3,-1] 。最高海拔为 0 。

说明:

  • n == gain.length
  • 1 <= n <= 100
  • -100 <= gain[i] <= 100

思路

n + 1 个海拔点 altitudealtitude[0] = 0gain[i] 表示从海拔点 ii + 1 的增量,即 altitude[i + 1] = altitude[i] + gain[i],返回最大的海拔。

依题意模拟即可。

代码


/**
 * @date 2026-06-19 8:08
 */
public class LargestAltitude1732 {

    public int largestAltitude(int[] gain) {
        int altitude = 0;
        int res = 0;
        for (int g : gain) {
            altitude += g;
            res = Math.max(res, altitude);
        }
        return res;
    }
}

性能

2574.左右元素和的差值

目标

给你一个下标从 0 开始的长度为 n 的整数数组 nums。

定义两个数组 leftSum 和 rightSum,其中:

  • leftSum[i] 是数组 nums 中下标 i 左侧元素之和。如果不存在对应的元素,leftSum[i] = 0 。
  • rightSum[i] 是数组 nums 中下标 i 右侧元素之和。如果不存在对应的元素,rightSum[i] = 0 。

返回长度为 n 数组 answer,其中 answer[i] = |leftSum[i] - rightSum[i]|。

示例 1:

输入:nums = [10,4,8,3]
输出:[15,1,11,22]
解释:数组 leftSum 为 [0,10,14,22] 且数组 rightSum 为 [15,11,3,0] 。
数组 answer 为 [|0 - 15|,|10 - 11|,|14 - 3|,|22 - 0|] = [15,1,11,22] 。

示例 2:

输入:nums = [1]
输出:[0]
解释:数组 leftSum 为 [0] 且数组 rightSum 为 [0] 。
数组 answer 为 [|0 - 0|] = [0] 。

说明:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i] <= 10^5

思路

有一个正整数数组 numsleftSum[i] 表示下标 i 左侧的元素之和,rightSum[i] 表示下标 i 右侧的元素之和,返回结果数组 answeranswer[i] = abs(leftSum[i] - rightSum[i])

依题意模拟

  • totalSum = leftSum[i] + nums[i] + rightSum[i]
  • answer[i] = abs(leftSum[i] - (totalSum - leftSum[i] - nums[i])) = abs(leftSum[i] + leftSum[i + 1] - totalSum) = abs(leftSum[i] + leftSum[i + 1] - leftSum[n])

计算出前缀和,然后根据返填答案即可。

代码


/**
 * @date 2026-06-08 11:59
 */
public class LeftRightDifference2574 {

    public int[] leftRightDifference(int[] nums) {
        int n = nums.length;
        int[] res = new int[n];
        int[] prefix = new int[n + 1];
        for (int i = 0; i < n; i++) {
            prefix[i + 1] = prefix[i] + nums[i];
        }
        for (int i = 0; i < n; i++) {
            res[i] = Math.abs(prefix[i] - (prefix[n] - prefix[i + 1]));
        }
        return res;
    }
}

性能

1871.跳跃游戏VII

目标

给你一个下标从 0 开始的二进制字符串 s 和两个整数 minJump 和 maxJump 。一开始,你在下标 0 处,且该位置的值一定为 '0' 。当同时满足如下条件时,你可以从下标 i 移动到下标 j 处:

  • i + minJump <= j <= min(i + maxJump, s.length - 1) 且
  • s[j] == '0'.

如果你可以到达 s 的下标 s.length - 1 处,请你返回 true ,否则返回 false 。

示例 1:

输入:s = "011010", minJump = 2, maxJump = 3
输出:true
解释:
第一步,从下标 0 移动到下标 3 。
第二步,从下标 3 移动到下标 5 。

示例 2:

输入:s = "01101110", minJump = 2, maxJump = 3
输出:false

说明:

  • 2 <= s.length <= 10^5
  • s[i] 要么是 '0' ,要么是 '1'
  • s[0] == '0'
  • 1 <= minJump <= maxJump < s.length

思路

有一个长度为 n 的二进制字符串 s,开始在位置 0 且该位置的值为 0,从该位置出发每次可以跳跃到 [i + minJump, min(i + maxJump, n - 1)] 中元素值为 0 的位置 j,判断能否到达 n - 1

定义 dp[i] 表示能否到达下标 i,如果 dp[i] = true,标记 [Math.max(j, i + minJump), Math.min(n - 1, i + maxJump)] 中元素值为 '0' 的下标,其中 j 表示之前可覆盖的最大下标 + 1。

代码


/**
 * @date 2026-05-25 8:50
 */
public class CanReach1871 {

    public boolean canReach(String s, int minJump, int maxJump) {
        int n = s.length();
        char[] chars = s.toCharArray();
        if (chars[n - 1] != '0') {
            return false;
        }
        boolean[] dp = new boolean[n];
        dp[0] = true;
        int j = 1;
        for (int i = 0; i < n; i++) {
            if (dp[i]) {
                for (j = Math.max(j, i + minJump); j <= Math.min(n - 1, i + maxJump); j++) {
                    dp[j] = chars[j] == '0';
                }
            }
        }
        return dp[n - 1];
    }

}

性能

2615.等值距离和

目标

给你一个下标从 0 开始的整数数组 nums 。现有一个长度等于 nums.length 的数组 arr 。对于满足 nums[j] == nums[i] 且 j != i 的所有 j ,arr[i] 等于所有 |i - j| 之和。如果不存在这样的 j ,则令 arr[i] 等于 0 。

返回数组 arr 。

示例 1:

输入:nums = [1,3,1,1,2]
输出:[5,0,3,4,0]
解释:
i = 0 ,nums[0] == nums[2] 且 nums[0] == nums[3] 。因此,arr[0] = |0 - 2| + |0 - 3| = 5 。 
i = 1 ,arr[1] = 0 因为不存在值等于 3 的其他下标。
i = 2 ,nums[2] == nums[0] 且 nums[2] == nums[3] 。因此,arr[2] = |2 - 0| + |2 - 3| = 3 。
i = 3 ,nums[3] == nums[0] 且 nums[3] == nums[2] 。因此,arr[3] = |3 - 0| + |3 - 2| = 4 。 
i = 4 ,arr[4] = 0 因为不存在值等于 2 的其他下标。

示例 2:

输入:nums = [0,5,3]
输出:[0,0,0]
解释:因为 nums 中的元素互不相同,对于所有 i ,都有 arr[i] = 0 。

说明:

  • 1 <= nums.length <= 10^5
  • 0 <= nums[i] <= 10^9

思路

有一个整数数组 nums,返回一个等长数组 arrarr[i] 等于所有与 nums[i] 相等的 nums[j] 到下标 i 的距离之和,即 Σ|i - j|,如果不存在这样的 j 则距离为 0

使用哈希表将元素分组,假定某一组内的元素为 a0 < a1 < …… < a_(m - 1),令 S0 = Σ_(j ∈ [0, m - 1])(aj - a0),考虑 S1 - S0,原来所有元素到 a0 的距离变成了所有元素到 a1 的距离:组内下标小于 1 的距离都增加了 a1 - a0,组内下标大于等于 1 的距离都减少了 a1 - a0

更一般的情况 Si - S_(i - 1),组内下标小于 i 的元素到 a_(i - 1) 的距离都增加了 a_i - a_(i - 1),组内下标大于等于 i 的元素到 a_(i - 1) 的距离都减小了 a_i - a_(i - 1)。组内下标小于 i 的元素有 i 个,组内下标大于等于 i 的元素有 m - i 个,Si - S_(i - 1) = i * (ai - a_(i - 1)) - (m - i) * (ai - a_(i - 1)) = (2 * i - m) * (ai - a_(i - 1))

代码


/**
 * @date 2026-04-23 9:46
 */
public class Distance2615 {

    public long[] distance(int[] nums) {
        int n = nums.length;
        long[] res = new long[n];
        Map<Integer, List<Integer>> map = new HashMap<>();
        for (int i = 0; i < n; i++) {
            map.putIfAbsent(nums[i], new ArrayList<>());
            map.get(nums[i]).add(i);
        }
        for (List<Integer> list : map.values()) {
            if (list.size() > 1) {
                long s = 0L;
                int start = list.get(0);
                for (int i = 1; i < list.size(); i++) {
                    s += list.get(i) - start;
                }
                res[start] = s;
                for (int i = 1; i < list.size(); i++) {
                    res[list.get(i)] = res[list.get(i - 1)] + (2 * i - list.size()) * (list.get(i) - list.get(i - 1));
                }
            }
        }
        return res;
    }
}

性能

3655.区间乘法查询后的异或II

目标

给你一个长度为 n 的整数数组 nums 和一个大小为 q 的二维整数数组 queries,其中 queries[i] = [li, ri, ki, vi]。

对于每个查询,需要按以下步骤依次执行操作:

  • 设定 idx = li。
  • 当 idx <= ri 时:
    • 更新:nums[idx] = (nums[idx] * vi) % (10^9 + 7)。
    • 将 idx += ki。

在处理完所有查询后,返回数组 nums 中所有元素的 按位异或 结果。

示例 1:

输入: nums = [1,1,1], queries = [[0,2,1,4]]
输出: 4
解释:
唯一的查询 [0, 2, 1, 4] 将下标 0 到下标 2 的每个元素乘以 4。
数组从 [1, 1, 1] 变为 [4, 4, 4]。
所有元素的异或为 4 ^ 4 ^ 4 = 4。

示例 2:

输入: nums = [2,3,1,5,4], queries = [[1,4,2,3],[0,2,1,2]]
输出: 31
解释:
第一个查询 [1, 4, 2, 3] 将下标 1 和 3 的元素乘以 3,数组变为 [2, 9, 1, 15, 4]。
第二个查询 [0, 2, 1, 2] 将下标 0、1 和 2 的元素乘以 2,数组变为 [4, 18, 2, 15, 4]。
所有元素的异或为 4 ^ 18 ^ 2 ^ 15 ^ 4 = 31。

说明:

  • 1 <= n == nums.length <= 10^5
  • 1 <= nums[i] <= 10^9
  • 1 <= q == queries.length <= 10^5
  • queries[i] = [li, ri, ki, vi]
  • 0 <= li <= ri < n
  • 1 <= ki <= n
  • 1 <= vi <= 10^5

思路

有一个长度为 n 的数组,对该数组执行 n 次查询,每次查询从 li 开始,对相距 ki 个位置上的元素执行 nums[idx] = (nums[idx] * vi) % (10^9 + 7) 直到下标 idx > ri。求处理完所有查询后 nums 中所有元素的 按位异或 结果。

可以参考差分数组的思想,采用商分数组。为每一个 ki 创建一个商分数组,需要更新的下标为 l、l + ki、l + 2 * ki、……、l + x * ki。如何确定最后一个下标?使用 r - l 将区间平移至 [0, r - l],距离右端点最近的距离为 (r - l) % k,因此原区间 [l, r] 的最后一个下标是 r - (r - l) % k。对于商分数组,需要在 l 处乘以 vi,在 r - (r - l) % k + k 处除以 vi。由于涉及到取模,这里需要求 vi 的逆元,根据 费马小定理 等价于计算 vi^(p - 2) % p,可以使用快速幂。

暴力解法的时间复杂度为 O(q * n / k),其中 q 为查询数组长度,nnums 长度,k 为所有查询中 ki 的均值,商分数组的时间复杂度为 O(q * logM + k * n)logM 为快速幂求逆元的时间复杂度,M = 10^9 + 7k * n 的复杂度用于遍历每一个 ki 的商分数组,内部是根据起点分组的 0 ~ ki - 1,步长为 ki

可以发现暴力解法的复杂度 k 越大越好,而商分数组的解法 k 越小越好。可以设置一个阈值 S,小于 S 使用商分数组,大于等于 S 使用暴力解法,时间复杂度为 O(q * n / S + S * n)。根据基本不等式 a + b >= 2sqrt(ab),当 a == b 时取等号,因此 q/S + S >= 2 * sqrt(q),当 S = sqrt(q) 时取得最小值。

代码

性能