2872.可以被K整除连通块的最大数目

目标

给你一棵 n 个节点的无向树,节点编号为 0 到 n - 1 。给你整数 n 和一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 有一条边。

同时给你一个下标从 0 开始长度为 n 的整数数组 values ,其中 values[i] 是第 i 个节点的 值 。再给你一个整数 k 。

你可以从树中删除一些边,也可以一条边也不删,得到若干连通块。一个 连通块的值 定义为连通块中所有节点值之和。如果所有连通块的值都可以被 k 整除,那么我们说这是一个 合法分割 。

请你返回所有合法分割中,连通块数目的最大值 。

示例 1:

输入:n = 5, edges = [[0,2],[1,2],[1,3],[2,4]], values = [1,8,1,4,4], k = 6
输出:2
解释:我们删除节点 1 和 2 之间的边。这是一个合法分割,因为:
- 节点 1 和 3 所在连通块的值为 values[1] + values[3] = 12 。
- 节点 0 ,2 和 4 所在连通块的值为 values[0] + values[2] + values[4] = 6 。
最多可以得到 2 个连通块的合法分割。

示例 2:

输入:n = 7, edges = [[0,1],[0,2],[1,3],[1,4],[2,5],[2,6]], values = [3,0,6,1,5,2,1], k = 3
输出:3
解释:我们删除节点 0 和 2 ,以及节点 0 和 1 之间的边。这是一个合法分割,因为:
- 节点 0 的连通块的值为 values[0] = 3 。
- 节点 2 ,5 和 6 所在连通块的值为 values[2] + values[5] + values[6] = 9 。
- 节点 1 ,3 和 4 的连通块的值为 values[1] + values[3] + values[4] = 6 。
最多可以得到 3 个连通块的合法分割。

说明:

  • 1 <= n <= 3 * 10^4
  • edges.length == n - 1
  • edges[i].length == 2
  • 0 <= ai, bi < n
  • values.length == n
  • 0 <= values[i] <= 10^9
  • 1 <= k <= 10^9
  • values 之和可以被 k 整除。
  • 输入保证 edges 是一棵无向树。

思路

有一个节点数量为 n 的无向树,树中节点之和可以被 k 整除,现在需要对树进行划分,要求每一个连通块中的节点和也能够被 k 整除,求最大的连通块个数。

dfs 遍历树,如果子树节点和能够整除 k 则可以与父节点断开,删除边数加 1,最终连通分量个数是删除的边数加 1。

代码


/**
 * @date 2025-11-28 9:31
 */
public class MaxKDivisibleComponents2872 {

    int res = 0;

    public int maxKDivisibleComponents(int n, int[][] edges, int[] values, int k) {
        List<Integer>[] g = new ArrayList[n];
        Arrays.setAll(g, x -> new ArrayList<>());
        for (int[] edge : edges) {
            g[edge[0]].add(edge[1]);
            g[edge[1]].add(edge[0]);
        }
        dfs(0, -1, g, values, k);
        return res + 1;
    }

    public int dfs(int i, int fa, List<Integer>[] g, int[] values, int k) {
        int sum = values[i];
        for (Integer next : g[i]) {
            if (next == fa) {
                continue;
            }
            int subSum = dfs(next, i, g, values, k);
            if (subSum % k == 0) {
                res++;
            } else {
                sum = (sum + subSum) % k;
            }
        }
        return sum % k;
    }

}

性能

2435.矩阵中和能被K整除的路径

目标

给你一个下标从 0 开始的 m x n 整数矩阵 grid 和一个整数 k 。你从起点 (0, 0) 出发,每一步只能往 下 或者往 右 ,你想要到达终点 (m - 1, n - 1) 。

请你返回路径和能被 k 整除的路径数目,由于答案可能很大,返回答案对 10^9 + 7 取余 的结果。

示例 1:

输入:grid = [[5,2,4],[3,0,5],[0,7,2]], k = 3
输出:2
解释:有两条路径满足路径上元素的和能被 k 整除。
第一条路径为上图中用红色标注的路径,和为 5 + 2 + 4 + 5 + 2 = 18 ,能被 3 整除。
第二条路径为上图中用蓝色标注的路径,和为 5 + 3 + 0 + 5 + 2 = 15 ,能被 3 整除。

示例 2:

输入:grid = [[0,0]], k = 5
输出:1
解释:红色标注的路径和为 0 + 0 = 0 ,能被 5 整除。

示例 3:

输入:grid = [[7,3,4,9],[2,3,6,2],[2,3,7,0]], k = 1
输出:10
解释:每个数字都能被 1 整除,所以每一条路径的和都能被 k 整除。

说明:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 5 * 10^4
  • 1 <= m n <= 5 10^4
  • 0 <= grid[i][j] <= 100
  • 1 <= k <= 50

思路

有一个矩阵 grid,从左上角出发,只能向下或向右走,求到达右下角的路径中,路径和能被 k 整除的路径数目。

定义 dp[i][j][r] 表示从 (0, 0) 到达 (i, j) 的路径和 模 k 余 r 的路径总数,状态转移方程为 dp[i][j][r] = (dp[i - 1][j][(r + k - rem) % k] + dp[i][j - 1][(r + k - rem) % k]) % mod,其中 rem = grid[i][j] % k。

代码


/**
 * @date 2025-11-26 8:49
 */
public class NumberOfPaths2435 {

    public int numberOfPaths(int[][] grid, int k) {
        int m = grid.length;
        int n = grid[0].length;
        int mod = 1000000007;
        int[][][] dp = new int[m][n][k];
        int sum = 0;
        for (int i = 0; i < m; i++) {
            sum += grid[i][0];
            dp[i][0][sum % k] = 1;
        }
        sum = 0;
        for (int j = 0; j < n; j++) {
            sum += grid[0][j];
            dp[0][j][sum % k] = 1;
        }
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                int rem = grid[i][j] % k;
                for (int r = 0; r < k; r++) {
                    dp[i][j][r] = (dp[i - 1][j][(r + k - rem) % k] + dp[i][j - 1][(r + k - rem) % k]) % mod;
                }
            }
        }
        return dp[m - 1][n - 1][0];
    }
}

性能

757.设置交集大小至少为2

目标

给你一个二维整数数组 intervals ,其中 intervals[i] = [starti, endi] 表示从 starti 到 endi 的所有整数,包括 starti 和 endi 。

包含集合 是一个名为 nums 的数组,并满足 intervals 中的每个区间都 至少 有 两个 整数在 nums 中。

  • 例如,如果 intervals = [[1,3], [3,7], [8,9]] ,那么 [1,2,4,7,8,9] 和 [2,3,4,8,9] 都符合 包含集合 的定义。

返回包含集合可能的最小大小。

示例 1:

输入:intervals = [[1,3],[3,7],[8,9]]
输出:5
解释:nums = [2, 3, 4, 8, 9].
可以证明不存在元素数量为 4 的包含集合。

示例 2:

输入:intervals = [[1,3],[1,4],[2,5],[3,5]]
输出:3
解释:nums = [2, 3, 4].
可以证明不存在元素数量为 2 的包含集合。 

示例 3:

输入:intervals = [[1,2],[2,3],[2,4],[4,5]]
输出:5
解释:nums = [1, 2, 3, 4, 5].
可以证明不存在元素数量为 4 的包含集合。 

说明:

  • 1 <= intervals.length <= 3000
  • intervals[i].length == 2
  • 0 <= starti < endi <= 10^8

思路

定义包含集合是 与 intervals 中每个区间的交集大小至少为 2 的集合,求包含集合的最小 size。

根据 intervals 中每个区间的起点排序,倒序遍历区间,对于最后一个区间,显然优先选最左边的 2 个元素最优,将其按照从大到小顺序加入包含列表,接着访问下一个区间,判断包含集合的最小的两个元素是否在当前区间内:

  • 如果都在,直接跳过
  • 如果都不在,将当前区间左侧前两个元素按从大到小顺序加入包含列表
  • 如果只有一个在,需要比较当前区间左端点 l 与包含集合最小元素 min 的关系,如果 min > l 将 l 加入列表,否则(即 min == l,由于是按起点排序的,所以 min 不会小于 l),用 l + 1 替换原来的列表最后一个元素,然后将 l 加入列表

代码


/**
 * @date 2025-11-20 8:46
 */
public class IntersectionSizeTwo757 {

    public int intersectionSizeTwo(int[][] intervals) {
        int n = intervals.length;
        Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
        List<Integer> res = new ArrayList<>();
        int p = 0;
        for (int i = n - 1; i >= 0; i--) {
            int l = intervals[i][0];
            int r = intervals[i][1];
            if (p == 0 || res.get(p - 1) > r) {
                res.add(l + 1);
                res.add(l);
                p += 2;
            } else if (p > 1 && res.get(p - 2) > r) {
                if (res.get(p - 1) == l) {
                    res.set(p - 1, l + 1);
                }
                res.add(l);
                p++;
            }
        }
        return res.size();
    }

}

性能

2528.最大化城市的最小电量

目标

给你一个下标从 0 开始长度为 n 的整数数组 stations ,其中 stations[i] 表示第 i 座城市的供电站数目。

每个供电站可以在一定 范围 内给所有城市提供电力。换句话说,如果给定的范围是 r ,在城市 i 处的供电站可以给所有满足 |i - j| <= r 且 0 <= i, j <= n - 1 的城市 j 供电。

  • |x| 表示 x 的 绝对值 。比方说,|7 - 5| = 2 ,|3 - 10| = 7 。

一座城市的 电量 是所有能给它供电的供电站数目。

政府批准了可以额外建造 k 座供电站,你需要决定这些供电站分别应该建在哪里,这些供电站与已经存在的供电站有相同的供电范围。

给你两个整数 r 和 k ,如果以最优策略建造额外的发电站,返回所有城市中,最小电量的最大值是多少。

这 k 座供电站可以建在多个城市。

示例 1:

输入:stations = [1,2,4,5,0], r = 1, k = 2
输出:5
解释:
最优方案之一是把 2 座供电站都建在城市 1 。
每座城市的供电站数目分别为 [1,4,4,5,0] 。
- 城市 0 的供电站数目为 1 + 4 = 5 。
- 城市 1 的供电站数目为 1 + 4 + 4 = 9 。
- 城市 2 的供电站数目为 4 + 4 + 5 = 13 。
- 城市 3 的供电站数目为 5 + 4 = 9 。
- 城市 4 的供电站数目为 5 + 0 = 5 。
供电站数目最少是 5 。
无法得到更优解,所以我们返回 5 。

示例 2:

输入:stations = [4,4,4,4], r = 0, k = 3
输出:4
解释:
无论如何安排,总有一座城市的供电站数目是 4 ,所以最优解是 4 。

说明:

  • n == stations.length
  • 1 <= n <= 10^5
  • 0 <= stations[i] <= 10^5
  • 0 <= r <= n - 1
  • 0 <= k <= 10^9

思路

有 n 个城市,stations[i] 表示第 i 个城市的供电站数目,供电站可以为 r 范围内的城市供电,城市的电量定义为能够为它供电的电站数量。现在计划在这 n 个城市中额外建立 k 座供电站,求所有城市中最小电量的最大值是多少。

最大化最小值考虑枚举答案。贪心策略,如果当前城市供电量小于下限,则需要在 i + r 处建厂,因为前面的都已经满足了,在这里建厂可以更多地提高后面的下限。

代码


/**
 * @date 2025-11-07 10:57
 */
public class MaxPower2528 {

    public long maxPower(int[] stations, int r, int k) {
        int n = stations.length;
        long[] diff = new long[n + 1];
        long max = 0;
        for (int i = 0; i < n; i++) {
            max = Math.max(max, stations[i]);
            int left = Math.max(0, i - r);
            int right = Math.min(n, i + r + 1);
            diff[left] += stations[i];
            diff[right] -= stations[i];
        }
        long left = 0, right = (max + k) * (r + 1);
        long m = left + (right - left) / 2;
        while (left <= right) {
            if (check(diff, m, k, r)) {
                left = m + 1;
            } else {
                right = m - 1;
            }
            m = left + (right - left) / 2;
        }

        return right;
    }

    public boolean check(long[] diff, long target, int k, int r) {
        long sum = 0;
        int n = diff.length;
        long[] tmp = new long[n];
        System.arraycopy(diff, 0, tmp, 0, n);
        for (int i = 0; i < n - 1; i++) {
            sum += tmp[i];
            if (sum >= target) {
                continue;
            }
            long c = target - sum;
            if (k >= c ) {
                tmp[Math.min(n - 1, i + r + r + 1)] -= c;
                sum = target;
                k -= c;
            } else {
                return false;
            }

        }
        return true;
    }

}

性能

3321.计算子数组的 x-sum II

目标

给你一个由 n 个整数组成的数组 nums,以及两个整数 k 和 x。

数组的 x-sum 计算按照以下步骤进行:

  • 统计数组中所有元素的出现次数。
  • 仅保留出现次数最多的前 x 个元素的每次出现。如果两个元素的出现次数相同,则数值 较大 的元素被认为出现次数更多。
  • 计算结果数组的和。

注意,如果数组中的不同元素少于 x 个,则其 x-sum 是数组的元素总和。

返回一个长度为 n - k + 1 的整数数组 answer,其中 answer[i] 是 子数组 nums[i..i + k - 1] 的 x-sum。

子数组 是数组内的一个连续 非空 的元素序列。

示例 1:

输入:nums = [1,1,2,2,3,4,2,3], k = 6, x = 2
输出:[6,10,12]
解释:
对于子数组 [1, 1, 2, 2, 3, 4],只保留元素 1 和 2。因此,answer[0] = 1 + 1 + 2 + 2。
对于子数组 [1, 2, 2, 3, 4, 2],只保留元素 2 和 4。因此,answer[1] = 2 + 2 + 2 + 4。注意 4 被保留是因为其数值大于出现其他出现次数相同的元素(3 和 1)。
对于子数组 [2, 2, 3, 4, 2, 3],只保留元素 2 和 3。因此,answer[2] = 2 + 2 + 2 + 3 + 3。

示例 2:

输入:nums = [3,8,7,8,7,5], k = 2, x = 2
输出:[11,15,15,15,12]
解释:
由于 k == x,answer[i] 等于子数组 nums[i..i + k - 1] 的总和。

说明:

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

思路

//todo

  • 295.数据流的中位数
  • 480.滑动窗口中位数
  • 3013.将数组分成最小总代价的子数组 II

代码

性能

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;
    }
}

性能

3003.执行操作后的最大分割数量

目标

给你一个下标从 0 开始的字符串 s 和一个整数 k。

你需要执行以下分割操作,直到字符串 s 变为 空:

  • 选择 s 的最长 前缀,该前缀最多包含 k 个 不同 字符。
  • 删除 这个前缀,并将分割数量加一。如果有剩余字符,它们在 s 中保持原来的顺序。

执行操作之 前 ,你可以将 s 中 至多一处 下标的对应字符更改为另一个小写英文字母。

在最优选择情形下改变至多一处下标对应字符后,用整数表示并返回操作结束时得到的 最大 分割数量。

示例 1:

输入:s = "accca", k = 2
输出:3
解释:
最好的方式是把 s[2] 变为除了 a 和 c 之外的东西,比如 b。然后它变成了 "acbca"。
然后我们执行以下操作:
1. 最多包含 2 个不同字符的最长前缀是 "ac",我们删除它然后 s 变为 "bca"。
2. 现在最多包含 2 个不同字符的最长前缀是 "bc",所以我们删除它然后 s 变为 "a"。
3. 最后,我们删除 "a" 并且 s 变成空串,所以该过程结束。
进行操作时,字符串被分成 3 个部分,所以答案是 3。

示例 2:

输入:s = "aabaab", k = 3
输出:1
解释:
一开始 s 包含 2 个不同的字符,所以无论我们改变哪个, 它最多包含 3 个不同字符,因此最多包含 3 个不同字符的最长前缀始终是所有字符,因此答案是 1。

示例 3:

输入:s = "xxyz", k = 1
输出:4
解释:
最好的方式是将 s[0] 或 s[1] 变为 s 中字符以外的东西,例如将 s[0] 变为 w。
然后 s 变为 "wxyz",包含 4 个不同的字符,所以当 k 为 1,它将分为 4 个部分。

说明:

  • 1 <= s.length <= 10^4
  • s 只包含小写英文字母。
  • 1 <= k <= 26

思路

有一个字符串 s 和一个整数 k,允许至多将 s 中的任意一个字符替换为其它小写英文字母,然后循环执行以下操作:删除字符串 s 的 最长 前缀,要求前缀中 最多 包含 k 个不同字符。求最大的删除次数。

// todo

代码

性能

3539.魔法序列的数组乘积之和

目标

给你两个整数 M 和 K,和一个整数数组 nums。

一个整数序列 seq 如果满足以下条件,被称为 魔法 序列:

  • seq 的序列长度为 M。
  • 0 <= seq[i] < nums.length
  • 2^seq[0] + 2^seq[1] + ... + 2^seq[M - 1] 的 二进制形式 有 K 个 置位。

这个序列的 数组乘积 定义为 prod(seq) = (nums[seq[0]] * nums[seq[1]] * ... * nums[seq[M - 1]])。

返回所有有效 魔法 序列的 数组乘积 的 总和 。

由于答案可能很大,返回结果对 10^9 + 7 取模。

置位 是指一个数字的二进制表示中值为 1 的位。

示例 1:

输入: M = 5, K = 5, nums = [1,10,100,10000,1000000]
输出: 991600007
解释:
所有 [0, 1, 2, 3, 4] 的排列都是魔法序列,每个序列的数组乘积是 1013。

示例 2:

输入: M = 2, K = 2, nums = [5,4,3,2,1]
输出: 170
解释:
魔法序列有 [0, 1],[0, 2],[0, 3],[0, 4],[1, 0],[1, 2],[1, 3],[1, 4],[2, 0],[2, 1],[2, 3],[2, 4],[3, 0],[3, 1],[3, 2],[3, 4],[4, 0],[4, 1],[4, 2] 和 [4, 3]。

示例 3:

输入: M = 1, K = 1, nums = [28]
输出: 28
解释:
唯一的魔法序列是 [0]。

说明:

  • 1 <= K <= M <= 30
  • 1 <= nums.length <= 50
  • 1 <= nums[i] <= 10^8

思路

代码

性能

1912.设计电影租借系统

目标

你有一个电影租借公司和 n 个电影商店。你想要实现一个电影租借系统,它支持查询、预订和返还电影的操作。同时系统还能生成一份当前被借出电影的报告。

所有电影用二维整数数组 entries 表示,其中 entries[i] = [shopi, moviei, pricei] 表示商店 shopi 有一份电影 moviei 的拷贝,租借价格为 pricei 。每个商店有 至多一份 编号为 moviei 的电影拷贝。

系统需要支持以下操作:

  • Search:找到拥有指定电影且 未借出 的商店中 最便宜的 5 个 。商店需要按照 价格 升序排序,如果价格相同,则 shopi 较小 的商店排在前面。如果查询结果少于 5 个商店,则将它们全部返回。如果查询结果没有任何商店,则返回空列表。
  • Rent:从指定商店借出指定电影,题目保证指定电影在指定商店 未借出 。
  • Drop:在指定商店返还 之前已借出 的指定电影。
  • Report:返回 最便宜的 5 部已借出电影 (可能有重复的电影 ID),将结果用二维列表 res 返回,其中 res[j] = [shopj, moviej] 表示第 j 便宜的已借出电影是从商店 shopj 借出的电影 moviej 。res 中的电影需要按 价格 升序排序;如果价格相同,则 shopj 较小 的排在前面;如果仍然相同,则 moviej 较小 的排在前面。如果当前借出的电影小于 5 部,则将它们全部返回。如果当前没有借出电影,则返回一个空的列表。

请你实现 MovieRentingSystem 类:

  • MovieRentingSystem(int n, int[][] entries) 将 MovieRentingSystem 对象用 n 个商店和 entries 表示的电影列表初始化。
  • List<Integer> search(int movie) 如上所述,返回 未借出 指定 movie 的商店列表。
  • void rent(int shop, int movie) 从指定商店 shop 借出指定电影 movie 。
  • void drop(int shop, int movie) 在指定商店 shop 返还之前借出的电影 movie 。
  • List<List<Integer>> report() 如上所述,返回最便宜的 已借出 电影列表。

注意:测试数据保证 rent 操作中指定商店拥有 未借出 的指定电影,且 drop 操作指定的商店 之前已借出 指定电影。

示例 1:

输入:
["MovieRentingSystem", "search", "rent", "rent", "report", "drop", "search"]
[[3, [[0, 1, 5], [0, 2, 6], [0, 3, 7], [1, 1, 4], [1, 2, 7], [2, 1, 5]]], [1], [0, 1], [1, 2], [], [1, 2], [2]]
输出:
[null, [1, 0, 2], null, null, [[0, 1], [1, 2]], null, [0, 1]]

解释:
MovieRentingSystem movieRentingSystem = new MovieRentingSystem(3, [[0, 1, 5], [0, 2, 6], [0, 3, 7], [1, 1, 4], [1, 2, 7], [2, 1, 5]]);
movieRentingSystem.search(1);  // 返回 [1, 0, 2] ,商店 1,0 和 2 有未借出的 ID 为 1 的电影。商店 1 最便宜,商店 0 和 2 价格相同,所以按商店编号排序。
movieRentingSystem.rent(0, 1); // 从商店 0 借出电影 1 。现在商店 0 未借出电影编号为 [2,3] 。
movieRentingSystem.rent(1, 2); // 从商店 1 借出电影 2 。现在商店 1 未借出的电影编号为 [1] 。
movieRentingSystem.report();   // 返回 [[0, 1], [1, 2]] 。商店 0 借出的电影 1 最便宜,然后是商店 1 借出的电影 2 。
movieRentingSystem.drop(1, 2); // 在商店 1 返还电影 2 。现在商店 1 未借出的电影编号为 [1,2] 。
movieRentingSystem.search(2);  // 返回 [0, 1] 。商店 0 和 1 有未借出的 ID 为 2 的电影。商店 0 最便宜,然后是商店 1 。

说明:

  • 1 <= n <= 3 * 10^5
  • 1 <= entries.length <= 10^5
  • 0 <= shopi < n
  • 1 <= moviei, pricei <= 10^4
  • 每个商店 至多 有一份电影 moviei 的拷贝。
  • search,rent,drop 和 report 的调用 总共 不超过 10^5 次。

思路

代码

性能