前缀和
使用一个数组sum来维护原数组a的前缀和,即sum[i] = a[1] + a[2] + ... + a[i]
前缀和其实非常简单,它的用处也无处不在。最主要的进行多次的区间求和,会在很多其他的算法中出现。
例如:求a[l...r]
的和,即sum[r] - sum[l - 1]
一维前缀和
static final int N = 100010;
static int[] sum = new int[N];
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n, m;
n = in.nextInt();
for(int i = 1; i <= n; i ++){
int x = in.nextInt();
sum[i] = x + sum[i - 1];
}
m = in.nextInt();
while(m --> 0){
int l, r;
l = in.nextInt();
r = in.nextInt();
System.out.println(sum[r] - sum[l - 1]);
}
}
变形
给你N个数,分别是a[1],a[2],...,a[n]。求一个最长的区间[x,y],使得区间中的数(a[x],a[x+1],a[x+2],...,a[y-1],a[y])的和能被7整除。输出区间长度。若没有符合要求的区间,输出0。
N (1≤N≤50,0001≤N≤50,000)
import java.util.*;
public class Main {
static final int N = 100010;
static int[] first = new int[]{0, -1, -1, -1, -1, -1, -1}, last = new int[7];
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n, sum = 0;
n = in.nextInt();
for(int i = 1; i <= n; i ++){
int x = in.nextInt();
sum = sum + x;
sum %= 7;
if(first[sum] == -1) first[sum] = i;
last[sum] = i;
}
int res = 0;
for(int i = 0; i <= 6; i ++){
if(first[i] != -1){
res = Math.max(last[i] - first[i], res);
}
}
System.out.println(res);
}
}
二维前缀和
代码实现:
import java.util.*;
public class Main{
static final int N = 10000;
static int[][] sum = new int[N][N];// sum[i][j]表示的是从(1,1)~(i,j)的子矩阵的和
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int n, m, q;
n = in.nextInt();
m = in.nextInt();
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= m;j ++){
int w = in.nextInt();
sum[i][j] = sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1] + w;
}
}
q = in.nextInt();
while(q --> 0){
int x1, y1, x2, y2;
x1 = in.nextInt();
y1 = in.nextInt();
x2 = in.nextInt();
y2 = in.nextInt();
System.out.println(sum[x2][y2] - sum[x2][y1 - 1] - sum[x1 - 1][y2] + sum[x1 - 1][y1 - 1]);
}
}
}
差分
使用d来维护使得a[i] = d[1] + d[2] + d[i]
的一个数组。即a
是d
的前缀和数组。差分与前缀和互为逆运算。
差分用的最多的地方就是,给一个区间的所有数进行多次加减同一个数,最后求改变后的数组。
不论是一维差分还是差分矩阵,我们不需要知道他是如何构造的,只需要知道他是如何修改的就行。并且我们无时无刻都要清楚差分数组的前缀和就是原数组,不论是一维还是二维。
一维差分
代码实现:
static final int N = 100010;
static int[] d = new int[N];
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n;
n = in.nextInt();
for(int i = 1; i <= n; i ++){
int k = in.nextInt();
insert(i, i, k);
}
for(int i = 1; i <= n; i ++){
System.out.print(d[i] + " ");
}
}
public static void insert(int l, int r, int k){
d[l] += k;
d[r + 1] -= k;
}
二维差分
代码实现:
import java.util.*;
public class Main{
static final int N = 10000;
static int[][] d = new int[N][N];
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int n, m;
n = in.nextInt();
m = in.nextInt();
// 构造差分矩阵
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= m; j ++){
int x = in.nextInt();
add(i, j, i, j, x);
}
}
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= m; j ++){
System.out.print(d[i][j] + " ");
}
System.out.println();
}
}
/**
* 给(x1,y1)~(x2,y2)的所有数都加上x
* @param x1
* @param y1
* @param x2
* @param y2
* @param x
*/
public static void add(int x1, int y1, int x2, int y2, int x){
d[x1][y1] += x;
d[x1][y2 + 1] -= x;
d[x2 + 1][y1] -= x;
d[x2 + 1][y2 + 1] += x;
}
}