1. 常用套路:

:::info TreeMap + 差分
用TreeMap是为了方便求前缀和 ::: :::info 第二种方法是扫描线,利用排序来做 :::

2. 例题

Acwing 1952. 金发姑娘和 N 头牛
思路:
考虑如果温度的范围没那个大的话如何计算?
直接使用差分数组,修改区间,当然这里是有两段区间(这一点和别的题一段区间稍稍有点区别,以前没见过)。然后遍历数组,前缀和就是当前温度下的最大产奶量,找到最大值即可。
考虑本题,温度范围太大,直接用数组存储不现实,但是奶牛的数量有限,故温度的边界是有限的,所有能使用离散化。

把每头奶牛在不同温度下的产奶量列出来,可以构成一个n*T的矩阵,其中n表示奶牛数量,T表示最大温度。就像下面的矩阵一样。
XXXYYYZZZZ
XXYYYZZZZZ
XYYYYZZZZZ
遍历map求前缀和,相当于求当前温度对应的列和。
我们的目标就是要找到和最大的一列的和。
一个问题:能保证只遍历map中的键对应的列就能找到最佳温度吗?
答:能,如果最佳温度不是某个键,说明存在一个距该温度最近的键,该列与最佳温度对应的前缀和应该是一致的。 :::tips 本质:区间修改,单点查询 :::

  1. import java.util.*;
  2. public class Main {
  3. public static void main(String[] args) {
  4. Scanner sc = new Scanner(System.in);
  5. int n = sc.nextInt(), x = sc.nextInt(), y = sc.nextInt(), z = sc.nextInt();
  6. TreeMap<Integer, Integer> map = new TreeMap<>();
  7. for (int i = 0; i < n; i++) {
  8. int l = sc.nextInt();
  9. int r = sc.nextInt();
  10. map.merge(-1, x, Integer::sum);
  11. map.merge(l, y - x, Integer::sum);
  12. map.merge(r + 1, z - y, Integer::sum);
  13. }
  14. int max = 0, sum = 0;
  15. for (int key : map.keySet()) {
  16. sum += map.get(key);
  17. max = Math.max(max, sum);
  18. }
  19. System.out.println(max);
  20. }
  21. }
  1. // 扫描线方法

类似的题目还有:

Acwing. 1987. 粉刷栅栏 离散化+ 差分
Acwing 906. 区间分组 离散化+ 差分
1526. 形成目标数组的子数组最少增加次数 算半个差分思想
Acwing. 101. 最高的牛 差分
1109. 航班预订统计 差分
1094. 拼车 差分/扫描线
253. 会议室 II 差分/扫描线
798. 得分最高的最小轮调 差分

798. 得分最高的最小轮调
思路:整体考虑很困难(考虑每个区间的得分元素个数),而分开考虑较为简单(考虑每一个元素的得分区间)
考虑下标为i的元素x(0 <= x < nums.length)在哪段区间内能得分
x < i 成立,说明得分区间在i的左右两段包括i
此时得分区间的范围是[x, n - 1],对应于最小轮调为0 <= k <= i - x, i - (n - 1) <= k < 0
x >= i成立,说明得分区间在i的右半段(可能包括i)
此时得分区间的范围是[x, n - 1],对应的最小轮调为i - (n - 1) <= k <= i - x
根据xi的相对大小,需保证k为非负整数(在mod n意义下)

  1. class Solution {
  2. public int bestRotation(int[] nums) {
  3. int n = nums.length;
  4. int[] diff = new int[n + 1];
  5. for (int i = 0; i < n; i++) {
  6. if (nums[i] <= i) {
  7. diff[0] += 1;
  8. diff[i - nums[i] + 1] -= 1;
  9. diff[i + 1] += 1;
  10. diff[n] -= 1;
  11. } else {
  12. diff[i + 1] += 1;
  13. diff[i - nums[i] + n + 1] -= 1;
  14. }
  15. }
  16. int max = 0, res = -1;
  17. int s = 0;
  18. for (int i = 0; i < n; i++) {
  19. s += diff[i];
  20. if (s > max) {
  21. max = s;
  22. res = i;
  23. }
  24. }
  25. return res;
  26. }
  27. }