差分法的使用
1.引言

问题提出:对一组数据,将第m个到第n个数据之间的数据全部加a,如果问题规模很小,用for循环是可以解决的,但如果问题规模很大,对不同的m,n,a操作n次,继续用for循环就比较耗时耗力了,为此,这里就有了差分法的使用

2.原理实现
  • 对于一组初始的数据f=1,2,3,4,5,6,7 ,f共有7个数据,我们用length为7+2的数组存储该数据,注意,f[0]和f[8]为默认值0,f[1] = 1,f[2] = 2,f[3] = 3,f[4] = 4,f[5] = 5,f[6] = 6,f[7] = 7
  • 然后我们构建它的差分数组d, d[0]和d[8]为0,然后从下标为1到下标为7,d[i] = f[i] + f[i - 1],所以d[0] = 0,d[1] = 1,d[2] = 1,d[3] = 1,d[4] = 1,d[5] = 1,d[6] = 1,d[7] = 1,d[8] = 0
  • 因为d[i] = f[i] + f[i - 1],所以原来的f[i] = d[i] + f[i - 1],比如现在给定2~5之间的数据均+1,我们可以发现,只要让d[2]+1,就能保证根据f[i] = d[i] + f[i - 1]还原出来的f[i]从第2个数据开始都加了1,因为还限制了右界范围,所以还要让d[5 + 1] - 1, 以满足第六个数据开始并不+1
  • 最后,总的来说就是构建原数据f的差分数据d,对于每次给定的m,n a,只需让d[m] + a,d[n + 1] -a,最后根据f[i] = d[i] + f[i - 1],更新f,f即为最后所求
  • 【注意】之所以有length个数据,要创建length+2个大小的数组,是因为下标为0的位置用于操作d[1] = f[1] - f[0],而下标为n + 1 的位置用于操作 d[n+1] - a 的操作,当范围右界n恰好为最后一个数据时,仍需保证有第n+1个数据去 - a
3.代码模板
import java.util.Scanner;
//差分法
//输入一个长度为 n 的整数序列。
//接下来输入 m 个操作,每个操作包含三个整数 l,r,c,表示将序列中 [l,r] 之间的每个数加上 c。
//请你输出进行完所有操作后的序列。
public class Ac_T797_Differ {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
//        注意数组长度,应当为n+2
        int []arr = new int[n + 2];
        int []b = new int[n + 2];
        int m = scanner.nextInt();
//        将初始数据输入数组arr
        for (int i = 1; i < n + 1; i++) {
            arr[i] = scanner.nextInt();
        }
//        构造数组arr的差分数列b
        for (int i = 1; i < n + 1; i++) {
            b[i] = arr[i] - arr[i - 1];
        }
//        区间操作,b的左端位置+c,右端+1位置-c
        while(m-- >0){
            int l = scanner.nextInt();
            int r = scanner.nextInt();
            int c = scanner.nextInt();
            b[l] += c;
            b[r + 1] -= c;
        }
        for (int i = 1; i < n + 1; i++) {
            arr[i] = arr[i - 1] + b[i];
        }
        for (int i = 1; i < n + 1; i++) {
            System.out.print(arr[i] + " ");
        }
    }
}
4.实战演练
(1)题目描述(Ac_T2041):

开始时,共有 NN 个空干草堆,编号 1∼N1∼N。

约翰给贝茜下达了 KK 个指令,每条指令的格式为 A B,这意味着贝茜要在 A…BA…B 范围内的每个干草堆的顶部添加一个新的干草捆。

例如,如果贝茜收到指令 10 13,则她应在干草堆 10,11,12,1310,11,12,13 中各添加一个干草捆。

在贝茜完成了所有指令后,约翰想知道 NN 个干草堆的中值高度——也就是说,如果干草堆按照高度从小到大排列,位于中间的干草堆的高度。

方便起见,NN 一定是奇数,所以中间堆是唯一的。

请帮助贝茜确定约翰问题的答案。

输入格式

第一行包含 NN 和 KK。

接下来 KK 行,每行包含两个整数 A,BA,B,用来描述一个指令。

输出格式

输出完成所有指令后,NN个干草堆的中值高度。

数据范围

1≤N≤1061≤N≤106,
1≤K≤250001≤K≤25000,
1≤A≤B≤N

(2)代码实现(用for循环超出时间限制,只能用差分)
package Acwing;

import java.util.Arrays;
import java.util.Scanner;
//干草堆
//共有 N 个空干草堆,编号 1∼N
// K 个指令,每条指令的格式为 A B,这意味着贝茜要在 A..B 范围内的每个干草堆的顶部添加一个新的干草捆。
//完成了所有指令后,约翰想知道 N 个干草堆的中值高度
//N 一定是奇数
//差分思想
public class Test {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
//        n为草堆个数,k为指令个数
        int n = scanner.nextInt();
        int k = scanner.nextInt();
        int []arr = new int[n+2];
//        while循环控制指区间加1的次数,即指令次数
        while(k-- != 0){
            int a = scanner.nextInt();
            int b = scanner.nextInt();
//            差分法最左边边界+1,最右边的下一个位置-1
            arr[a] += 1;
            arr[b+1] -= 1;
        }
        for(int i = 1; i <= n; i++){
            arr[i] += arr[i-1];
        }
//        排序
        Arrays.sort(arr);
//        输出中位数
        System.out.println(arr[arr.length / 2 + 1]);
    }

}