前缀和

1.一维模板题

AcWing 795. 前缀和(算法基础课) - AcWing

输入一个长度为 n的整数序列。

接下来再输入 m 个询问,每个询问输入一对 l,r。

对于每个询问,输出原序列中从第 l 个数到第 r个数的和。

输入格式

第一行包含两个整数 n 和 m。

第二行包含 n 个整数,表示整数数列。

接下来 m 行,每行包含两个整数 l 和 r,表示一个询问的区间范围。

输出格式

共 m 行,每行输出一个询问的结果。

数据范围

1≤l≤r≤n1,  
1≤n,m≤1000001,  
−1000≤数列中元素的值≤1000

输入样例:

1
2
3
4
5
5 3
2 1 3 6 4
1 2
1 3
2 4

输出样例:

1
2
3
3
6
10

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
* 795. 前缀和
* https://www.acwing.com/problem/content/797/
*
* @author yangxiaozhuo
* @date 2023/02/16
*/
public class Main02 {
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String[] s = in.readLine().split(" ");
int n = Integer.parseInt(s[0]);
int m = Integer.parseInt(s[1]);
int[] num = new int[n + 1];
s = in.readLine().split(" ");
for (int i = 0; i < n; i++) {
num[i + 1] = num[i] + Integer.parseInt(s[i]);
}
while (m-- > 0) {
s = in.readLine().split(" ");
int a = Integer.parseInt(s[0]);
int b = Integer.parseInt(s[1]);
System.out.println(num[b] - num[a - 1]);
}
}
}

2.二维模板题

796. 子矩阵的和 - AcWing题库

输入一个 n 行 m 列的整数矩阵,再输入 q 个询问,每个询问包含四个整数 x1,y1,x2,y2表示一个子矩阵的左上角坐标和右下角坐标。

对于每个询问输出子矩阵中所有数的和。

输入格式

第一行包含三个整数 n,m,q。

接下来 n行,每行包含 m 个整数,表示整数矩阵。

接下来 q行,每行包含四个整数 x1,y1,x2,y2,表示一组询问。

输出格式

共 q 行,每行输出一个询问的结果。

数据范围

1≤n,m≤10001
1≤q≤200000
1≤x1≤x2≤n
1≤y1≤y2≤m
−1000≤矩阵内元素的值≤1000

输入样例:

1
2
3
4
5
6
7
3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4

输出样例:

1
2
3
17
27
21

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
* 796. 子矩阵的和
* https://www.acwing.com/problem/content/798/
*
* @author yangxiaozhuo
* @date 2023/02/16
*/
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String[] s = in.readLine().split(" ");
int n = Integer.parseInt(s[0]);
int m = Integer.parseInt(s[1]);
int q = Integer.parseInt(s[2]);
int[][] num = new int[n + 1][m + 1];
for (int i = 1; i <= n; i++) {
s = in.readLine().split(" ");
for (int j = 1; j <= m; j++) {
num[i][j] = num[i - 1][j] + num[i][j - 1] - num[i - 1][j - 1] + Integer.parseInt(s[j - 1]);
}
}
while (q-- > 0) {
s = in.readLine().split(" ");
int x1 = Integer.parseInt(s[0]);
int y1 = Integer.parseInt(s[1]);
int x2 = Integer.parseInt(s[2]);
int y2 = Integer.parseInt(s[3]);
System.out.println(num[x2][y2] - num[x2][y1 - 1] - num[x1 - 1][y2] + num[x1 - 1][y1 - 1]);
}
}
}

3.一维训练题

3956. 截断数组 - AcWing题库

给定一个长度为 n 的数组 a1,a2,…,an

现在,要将该数组从中间截断,得到三个非空子数组。

要求,三个子数组内各元素之和都相等。

请问,共有多少种不同的截断方法?

输入格式

第一行包含整数 n。

第二行包含 n 个整数 a1,a2,…,an

输出格式

输出一个整数,表示截断方法数量。

数据范围

前六个测试点满足 1≤n≤10
所有测试点满足 1≤n≤105,−10000≤ai≤10000

输入样例:

1
2
4
1 2 3 3

输出样例:

1
1

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
* 3956. 截断数组
* https://www.acwing.com/problem/content/3959/
*
* @author yangxiaozhuo
* @date 2023/02/16
*/
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(in.readLine());
String[] s = in.readLine().split(" ");
if(n < 3) {
System.out.println(0);
return;
}
int[] nums = new int[n + 1];
for(int i = 0 ; i < n; i++) {
nums[i + 1] = nums[i] + Integer.parseInt(s[i]);
}
if(nums[n] % 3 != 0) {
System.out.println(0);
return;
}
long res = 0;
long cnt = 0;
for(int i = 2; i < n; i++) {
if(nums[i - 1] == nums[n] / 3) {
cnt++;
}
if(nums[i] == nums[n] / 3 * 2) {
res += cnt;
}
}
System.out.println(res);
}
}

4.一维蓝桥杯真题省赛B组

1230. K倍区间 - AcWing题库

给定一个长度为 N 的数列,A1,A2,…AN,如果其中一段连续的子序列 Ai,Ai+1,…Aj之和是 K 的倍数,我们就称这个区间 [i,j] 是 K 倍区间。

你能求出数列中总共有多少个 K倍区间吗?

输入格式

第一行包含两个整数 N 和 K。

以下 N 行每行包含一个整数 Ai。

输出格式

输出一个整数,代表 K 倍区间的数目。

数据范围

1≤N,K≤100000
1≤Ai≤100000

输入样例:

1
2
3
4
5
6
5 2
1
2
3
4
5

输出样例:

1
6

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
* 1230. K倍区间
* https://www.acwing.com/problem/content/1232/
* @author yangxiaozhuo
* @date 2023/02/16
*/
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String[] s = in.readLine().split(" ");
int n = Integer.parseInt(s[0]);
int k = Integer.parseInt(s[1]);
long[] num = new long[n + 1];
long[] pre = new long[k + 1];
long res = 0;
for (int i = 1; i <= n; i++) {
num[i] = num[i - 1] + Integer.parseInt(in.readLine());
int t = (int)(num[i] % k);
res += pre[t];
pre[t]++;
if(t == 0) {
res++;
}
}
System.out.println(res);
}
}

5.二维算法竞赛进阶指南

99. 激光炸弹 - AcWing题库

地图上有 N 个目标,用整数 Xi,Yi表示目标在地图上的位置,每个目标都有一个价值 Wi。

注意:不同目标可能在同一位置。

现在有一种新型的激光炸弹,可以摧毁一个包含 R×R个位置的正方形内的所有目标。

激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆炸范围,即那个正方形的边必须和 x,y轴平行。

求一颗炸弹最多能炸掉地图上总价值为多少的目标。

输入格式

第一行输入正整数 N 和 R,分别代表地图上的目标数目和正方形包含的横纵位置数量,数据用空格隔开。

接下来 N 行,每行输入一组数据,每组数据包括三个整数 Xi,Yi,Wi,分别代表目标的 x 坐标,y 坐标和价值,数据用空格隔开。

输出格式

输出一个正整数,代表一颗炸弹最多能炸掉地图上目标的总价值数目。

数据范围

0≤R≤109
0<N≤10000
0≤Xi,Yi≤5000
0≤Wi≤1000

输入样例:

1
2
3
2 1
0 0 1
1 1 1

输出样例:

1
1

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
* 99. 激光炸弹
* https://www.acwing.com/problem/content/101/
*
* @author yangxiaozhuo
* @date 2023/02/16
*/
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String[] s = in.readLine().split(" ");
int n = Integer.parseInt(s[0]);
int r = Integer.parseInt(s[1]);
long[][] num = new long[5001][5001];
long[][] sum = new long[5002][5002];
for (int i = 0; i < n; i++) {
s = in.readLine().split(" ");
int x = Integer.parseInt(s[0]);
int y = Integer.parseInt(s[1]);
int w = Integer.parseInt(s[2]);
num[x][y] += w;
}
for (int i = 0; i < 5001; i++) {
for (int j = 0; j < 5001; j++) {
sum[i + 1][j + 1] = sum[i + 1][j] + sum[i][j + 1] - sum[i][j] + num[i][j];
}
}
long res = 0;
if (r >= 5000) {
System.out.println(sum[5001][5001]);
} else {
r = r - 1;
for (int i = 1; i < 5002 - r; i++) {
for (int j = 1; j < 5002 - r; j++) {
res = Math.max(res, sum[i + r][j + r] - sum[i - 1][j + r] - sum[i + r][j - 1] + sum[i - 1][j - 1]);
}
}
System.out.println(res);
}
}
}