AcWing 795. 前缀和
【思路】
一维前缀和
S[i] = a[1] + a[2] + … a[i]
a[l] + … + a[r] = S[r] - S[l - 1] = S[r]-S[l]+a[l]
import java.util.*;
import java.io.*;
class Main{
static int N = 100010;
static int preSum [] = new int[N];
static int f[] = new int[N];
public static void main(String args[]) throws Exception{
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
String arg [] = bf.readLine().split(" "); //读入n 和 q
int n = Integer.parseInt(arg[0]), q = Integer.parseInt(arg[1]);
String data[] = bf.readLine().split(" ");//读入序列
for(int i = 1; i <= n; i ++) f[i] = Integer.parseInt(data[i - 1]);
for(int i = 1; i <= n; i++) preSum[i] = preSum[i - 1] + f[i]; //前缀和数组
while( q -- > 0){
String s[] = bf.readLine().split(" ");
int l = Integer.parseInt(s[0]), r = Integer.parseInt(s[1]);
System.out.println(preSum[r] - preSum[l - 1]);
}
}
}
【思路】
二维前缀和
S[i, j] = 第i行j列格子左上部分所有元素的和
以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]
import java.io.*;
public class Main{
public static void main(String args[]) throws Exception{
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
String s[] = bf.readLine().split(" ");
int n = Integer.parseInt(s[0]), m = Integer.parseInt(s[1]), k =Integer.parseInt(s[2]);
int [][]q = new int[n+1][m+1];
int [][]sum = new int[n+1][m+1];
for(int i = 1; i <= n; i++){
String strArr[] = bf.readLine().split(" ");
for (int j = 1; j <= m; j++) q[i][j] = Integer.parseInt(strArr[j-1]);
}
//用来暂时存放每一行的前缀和
int col [] = new int[m+1];
//前缀和二维数组
for(int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
col[j] = col[j - 1] + q[i][j];
sum[i][j] = sum[i-1][j] + col[j];
//System.out.print(sum[i][j] + " ");
}
//System.out.println();
}
while( k > 0){
String p[] = bf.readLine().split(" ");
int x1 = Integer.parseInt(p[0]), y1 = Integer.parseInt(p[1]), x2 =Integer.parseInt(p[2]), y2 = Integer.parseInt(p[3]);
//画个图就知道下面公式的意义了
int res = sum[x2][y2] - sum[x2][y1 - 1] - sum[x1 - 1][y2] + sum[x1 - 1][y1 -1];
System.out.println(res);
k --;
}
}
}
import java.util.Scanner;
class Main{
static int N = 1010;
static int f[][] = new int[N][N]; //原数据
static int preSum[][] = new int[N][N]; //前缀和数组
public static void main(String args[]){
Scanner reader = new Scanner(System.in);
int n = reader.nextInt(), m = reader.nextInt(), q = reader.nextInt();
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
f[i][j] = reader.nextInt();
//计算前缀和数组: preSum[i][j] = preSum[i - 1][j] + preSum[i][j - 1] - preSum[i - 1][j - 1] + f[i][j];
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
preSum[i][j] = preSum[i - 1][j] + preSum[i][j - 1] - preSum[i - 1][j - 1] + f[i][j];
while( q-- > 0){
int x1 = reader.nextInt(), y1 = reader.nextInt(), x2 = reader.nextInt(), y2 = reader.nextInt();
System.out.println(preSum[x2][y2] - preSum[x2][y1 - 1] - preSum[x1 - 1][y2] + preSum[x1 - 1][ y1 - 1]);
}
}
}
【题目描述】
ACWing 797. 差分
【思路】
给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c
对于数组a[i] = b[1] + b[2 ]+ b[3] +…… + b[i]
则a数组是b数组的前缀和数组,b数组叫做a数组的差分数组。差分数组为:
b[0] = a[0 ]= 0;
b[1] = a[1] - a[0];
b[2] = a[2] - a[1];
b[3] =a [3] - a[2];
…
b[n] = a[n] - a[n-1];
有了差分数组b,通过前缀和运算,就可以在O(n) 的时间内得到a数组。给定数组a,对a数组[l,r]区间中的每一个数加上c,常规做法是循环l到r加上c。执行m次操作,时间复杂度就为O(n*m)。
对于这个问题,就可以使用差分数组了:
给a数组中的[ l, r]区间中的每一个数都加上c,只需对差分数组b做 b[l] + = c, b[r+1] - = c。时间复杂度为O(1)。
import java.io.*;
class Main{
static int N = 100010;
static int [] b = new int[N];
static int [] a = new int[N];
//在原序列b [l,r]区间执行加c操作
public static void insert(int l, int r, int c){
b[l] += c;
b[r+1] -= c;
}
public static void main(String args[]) throws Exception{
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
String s [] = bf.readLine().split(" ");
int n = Integer.parseInt(s[0]), m = Integer.parseInt(s[1]);
String strArr[] = bf.readLine().split(" ");
for(int i = 1; i <= n; i++) a[i] = Integer.parseInt(strArr[i - 1]);
//构造差分数组
for(int i = 1; i <= n; i++) b[i] = a[i] - a[i - 1];
while(m > 0){
String p[] = bf.readLine().split(" ");
int l = Integer.parseInt(p[0]), r = Integer.parseInt(p[1]), c = Integer.parseInt(p[2]);
insert(l, r, c);
m--;
}
for( int i = 1; i <= n; i++) {
a[i] = a[i-1] + b[i]; //前缀和运算
System.out.print(a[i]+" ");
}
}
}
AcWing 798. 差分矩阵
【思路】
给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c:
S[x1, y1] += c, S[x2 + 1, y1] -= c, S[x1, y2 + 1] -= c, S[x2 + 1, y2 + 1] += c
import java.io.*;
public class Main{
static int N = 1010;
static int [][] b = new int[N][N];
public static void insert(int x1, int y1, int x2, int y2, int c){
b[x1][y1] += c;
b[x2 + 1][y1] -= c;
b[x1][ y2 + 1 ] -= c;
b[x2 + 1][y2 + 1] +=c;
}
public static void main(String args[]) throws Exception{
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));
String arg[]= bf.readLine().split(" ");
int n = Integer.parseInt(arg[0]), m = Integer.parseInt(arg[1]), q = Integer.parseInt(arg[2]);
int [][] a= new int[N][N];
for(int i = 1; i <= n; i++){
String s[] = bf.readLine().split(" ");
for(int j = 1; j <= m; j++) a[i][j] = Integer.parseInt(s[j - 1]);
}
//构建差分矩阵
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++) insert(i, j, i, j, a[i][j]);
}
while( q > 0){
String s[]= bf.readLine().split(" ");
int x1 = Integer.parseInt(s[0]), y1 = Integer.parseInt(s[1]), x2 = Integer.parseInt(s[2]),
y2 = Integer.parseInt(s[3]), c = Integer.parseInt(s[4]);
insert(x1, y1, x2, y2, c);
q--;
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){//二维前缀和
b[i][j] += b[i - 1][j] + b[i][j - 1] -b[i - 1][j - 1];
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){//二维前缀和
log.write(b[i][j] +" ");
}
log.write("\n");
}
log.flush();
bf.close();
log.close();
}
}