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) 时取得最小值。

代码

性能

1526.形成目标数组的子数组最少增加次数

目标

给你一个整数数组 target 和一个数组 initial ,initial 数组与 target 数组有同样的维度,且一开始全部为 0 。

请你返回从 initial 得到 target 的最少操作次数,每次操作需遵循以下规则:

  • 在 initial 中选择 任意 子数组,并将子数组中每个元素增加 1 。

答案保证在 32 位有符号整数以内。

示例 1:

输入:target = [1,2,3,2,1]
输出:3
解释:我们需要至少 3 次操作从 intial 数组得到 target 数组。
[0,0,0,0,0] 将下标为 0 到 4 的元素(包含二者)加 1 。
[1,1,1,1,1] 将下标为 1 到 3 的元素(包含二者)加 1 。
[1,2,2,2,1] 将下表为 2 的元素增加 1 。
[1,2,3,2,1] 得到了目标数组。

示例 2:

输入:target = [3,1,1,2]
输出:4
解释:(initial)[0,0,0,0] -> [1,1,1,1] -> [1,1,1,2] -> [2,1,1,2] -> [3,1,1,2] (target) 。

示例 3:

输入:target = [3,1,5,4,2]
输出:7
解释:(initial)[0,0,0,0,0] -> [1,1,1,1,1] -> [2,1,1,1,1] -> [3,1,1,1,1] 
                                  -> [3,1,2,2,2] -> [3,1,3,3,2] -> [3,1,4,4,2] -> [3,1,5,4,2] (target)。

示例 4:

输入:target = [1,1,1,1]
输出:1

说明:

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

思路

有一个 target 数组和一个与其维度相同的的全 0 数组 initial,每次操作可以将 initial 任意子数组的每个元素加 1。求将 initial 变为 target 的最小操作次数。

将子数组全部加一很容易想到差分数组,按照贪心的思想,要将元素从 0 操作为 target[i] 一定需要 target[i] 次,问题在于有多少个元素可以共用这一个操作。

计算差分数组,仅统计其中大于 0 的部分,小于等于 0 说明可以公用前面的操作,大于 0 说明需要增加操作。

代码


/**
 * @date 2025-10-30 8:57
 */
public class MinNumberOperations1526 {

    public int minNumberOperations(int[] target) {
        int n = target.length;
        int res = target[0];
        for (int i = 1; i < n; i++) {
            int diff = target[i] - target[i - 1];
            if (diff > 0) {
                res += diff;
            }
        }
        return res;
    }
}

性能

3347.执行操作后元素的最高频率II

目标

给你一个整数数组 nums 和两个整数 k 和 numOperations 。

你必须对 nums 执行 操作 numOperations 次。每次操作中,你可以:

  • 选择一个下标 i ,它在之前的操作中 没有 被选择过。
  • 将 nums[i] 增加范围 [-k, k] 中的一个整数。

在执行完所有操作以后,请你返回 nums 中出现 频率最高 元素的出现次数。

一个元素 x 的 频率 指的是它在数组中出现的次数。

示例 1:

输入:nums = [1,4,5], k = 1, numOperations = 2
输出:2
解释:
通过以下操作得到最高频率 2 :
将 nums[1] 增加 0 ,nums 变为 [1, 4, 5] 。
将 nums[2] 增加 -1 ,nums 变为 [1, 4, 4] 。

示例 2:

输入:nums = [5,11,20,20], k = 5, numOperations = 1
输出:2
解释:
通过以下操作得到最高频率 2 :
将 nums[1] 增加 0 。

说明:

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

思路

对数组 nums 执行 numOperations 次操作,每次操作可以选一个没有被操作过的元素,将其增加 [-k, k] 之间的整数,求元素值的最大出现次数。

3346.执行操作后元素的最高频率I 相比,本题的数据范围更大,无法枚举值域。

考虑使用差分数组,由于数据范围太大,使用有序集合来维护。差分数组的前缀就是可以操作成该元素的频率,它不能超过原来已有的个数 + 操作次数。

代码


/**
 * @date 2025-10-21 10:34
 */
public class MaxFrequency3347 {

    public int maxFrequency(int[] nums, int k, int numOperations) {
        Map<Integer, Integer> cnt = new HashMap<>();
        Map<Integer, Integer> diff = new TreeMap<>();
        for (int num : nums) {
            cnt.merge(num, 1, Integer::sum);
            diff.putIfAbsent(num, 0);
            diff.merge(num - k, 1, Integer::sum);
            diff.merge(num + k + 1, -1, Integer::sum);
        }
        int res = 0;
        int frequency = 0;
        for (Map.Entry<Integer, Integer> entry : diff.entrySet()) {
            frequency += entry.getValue();
            res = Math.max(res, Math.min(frequency, cnt.getOrDefault(entry.getKey(), 0) + numOperations));
        }
        return res;
    }
}

性能

2960.统计已测试设备

目标

给你一个长度为 n 、下标从 0 开始的整数数组 batteryPercentages ,表示 n 个设备的电池百分比。

你的任务是按照顺序测试每个设备 i,执行以下测试操作:

  • 如果 batteryPercentages[i] 大于 0:
    • 增加 已测试设备的计数。
    • 将下标在 [i + 1, n - 1] 的所有设备的电池百分比减少 1,确保它们的电池百分比 不会低于 0 ,即 batteryPercentages[j] = max(0, batteryPercentages[j] - 1)。
    • 移动到下一个设备。
  • 否则,移动到下一个设备而不执行任何测试。

返回一个整数,表示按顺序执行测试操作后 已测试设备 的数量。

示例 1:

输入:batteryPercentages = [1,1,2,1,3]
输出:3
解释:按顺序从设备 0 开始执行测试操作:
在设备 0 上,batteryPercentages[0] > 0 ,现在有 1 个已测试设备,batteryPercentages 变为 [1,0,1,0,2] 。
在设备 1 上,batteryPercentages[1] == 0 ,移动到下一个设备而不进行测试。
在设备 2 上,batteryPercentages[2] > 0 ,现在有 2 个已测试设备,batteryPercentages 变为 [1,0,1,0,1] 。
在设备 3 上,batteryPercentages[3] == 0 ,移动到下一个设备而不进行测试。
在设备 4 上,batteryPercentages[4] > 0 ,现在有 3 个已测试设备,batteryPercentages 保持不变。
因此,答案是 3 。

示例 2:

输入:batteryPercentages = [0,1,2]
输出:2
解释:按顺序从设备 0 开始执行测试操作:
在设备 0 上,batteryPercentages[0] == 0 ,移动到下一个设备而不进行测试。
在设备 1 上,batteryPercentages[1] > 0 ,现在有 1 个已测试设备,batteryPercentages 变为 [0,1,1] 。
在设备 2 上,batteryPercentages[2] > 0 ,现在有 2 个已测试设备,batteryPercentages 保持不变。
因此,答案是 2 。

说明:

  • 1 <= n == batteryPercentages.length <= 100
  • 0 <= batteryPercentages[i] <= 100

思路

如果设备的剩余电量大于0,已测试设备加1且后续设备的所有电量减1。

简而言之,后续设备需要减去的电量为已检测的设备数。

题解上说这是差分的思想。我们没必要检测一个设备就向后循环并将电量减1,当前设备需要减去的电量等于上一个设备需要减去的电量+1。

代码

/**
 * @date 2024-05-10 8:43
 */
public class CountTestedDevices2960 {
    public int countTestedDevices(int[] batteryPercentages) {
        int res = 0;
        for (int i = 0; i < batteryPercentages.length; i++) {
            if (batteryPercentages[i] > res) {
                res++;
            }
        }
        return res;
    }
}

性能