前缀和

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

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

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

输入格式

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

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

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

输出格式

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

个人代码

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
#include<bits/stdc++.h>
using namespace std;
int main() {
int n,m;
cin>>n>>m;
vector<int>num(n,0);
for(int i=0;i<n;i++){
cin>>num[i];
}
for(int i=1;i<n;i++){
num[i] += num[i-1];
}
int left,right;
// 考虑到后续计算的繁杂可以考虑将0点舍弃,vector 可能没有int[] 耗时少也可以考虑替换
while(m--){
cin>>left>>right;
int all = num[right - 1];
if (left != 1) {
all -= num[left - 2];
}
cout<<all<<endl;
}

}
/*
5 3
2 1 3 6 4
1 2
1 3
2 4

*/

板子代码:

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
#include <iostream>

using namespace std;

const int N = 100010;

int n, m;
int a[N], s[N];

int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);

for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + a[i]; // 前缀和的初始化

while (m -- )
{
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", s[r] - s[l - 1]); // 区间和的计算
}

return 0;
}

796. 子矩阵的和 - AcWing题库

矩阵前缀和

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

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

输入格式

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

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

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

输出格式

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

个人代码

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
#include<bits/stdc++.h>
using namespace std;

int main() {
int n,m,p;
scanf("%d %d %d",&n,&m,&p);
vector<vector<int>>record(n+1,vector<int>(m+1,0));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>record[i][j];
record[i][j] += (record[i-1][j] + record[i][j-1] - record[i-1][j-1]);
}
}
while(p--){
int x,y,x1,y1;
scanf("%d %d %d %d",&x,&y,&x1,&y1);
cout<<record[x1][y1] + record[x-1][y-1] - record[x-1][y1] - record[x1][y-1]<<endl;
// 本题的难点在于算式

111111111111111111
111111111111111111
111111111211111111
111111111111111111
111111111111131111
111111111111111111
如果我们想获取2-3的和 即为从零点到3 所有的和减去左侧多算的数即为【3.x,2.y-1】下策多算的为【2.x-1,3.y】 即可个人失误点也是在这

}
}

/*

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
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
#include <iostream>

using namespace std;

const int N = 1010;

int n, m, q;
int s[N][N];

int main()
{
scanf("%d%d%d", &n, &m, &q);

for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
scanf("%d", &s[i][j]);

for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];

while (q -- )
{
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
printf("%d\n", s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]);
}

return 0;
}

差分

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

接下来输入 m� 个操作,每个操作包含三个整数 l,r,c�,�,�,表示将序列中 [l,r][�,�] 之间的每个数加上 c�。

请你输出进行完所有操作后的序列。

输入格式

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

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

接下来 m� 行,每行包含三个整数 l,r,c�,�,�,表示一个操作。

输出格式

共一行,包含 n� 个整数,表示最终序列。

数据范围

1≤n,m≤1000001≤�,�≤100000,
1≤l≤r≤n1≤�≤�≤�,
−1000≤c≤1000−1000≤�≤1000,
−1000≤整数序列中元素的值≤1000

1
2
3
4
<个人思路>n*m
<1 时间复杂度="n*m">暴力遍历 m * n</1>
<2 时间复杂度="n*n">如果n很小简历一个dp从上到下计算出对于每一个小范围的处理</2>
</个人思路>

板子代码

个人

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
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;

int b[N],a[N];
void insert(int l,int r, int v) {
b[l]+=v;
b[r+1]-=v;
}
int main() {
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
int val;cin>>val;
insert(i,i,val);
}
while(m--){
int left,right,val;
cin>>left>>right>>val;
insert(left, right, val);
}
for(int i=1;i<=n;i++){
b[i] += b[i-1];
cout<<b[i]<<" ";
}
}

板子

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
#include <iostream>

using namespace std;

const int N = 100010;

int n, m;
int a[N], b[N];

void insert(int l, int r, int c)
{
b[l] += c;
b[r + 1] -= c;
}

int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);

for (int i = 1; i <= n; i ++ ) insert(i, i, a[i]);

while (m -- )
{
int l, r, c;
scanf("%d%d%d", &l, &r, &c);
insert(l, r, c);
}

for (int i = 1; i <= n; i ++ ) b[i] += b[i - 1];

for (int i = 1; i <= n; i ++ ) printf("%d ", b[i]);

return 0;
}

/*
思想:之激烈变化不记录具体的数据,对于顺序输出有较好的作用,我们b[i]中的值是指对于 第i个元素比第i-i个元素大多少的意义,勇士结尾通过b[r+1]-v 消除掉对以后的影响,

*/

差分矩阵

个人思路

矩阵本身上是一个一个的数组我们可以只关注于一行

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
#include<bits/stdc++.h>
using namespace std;
const int N = 10010;
int record[N][N];
void inject(int lx,int ly, int rx, int ry, int val) {
for(int i=lx;i<=rx;i++){
record[i][ly] += val;
record[i][ry+1] -= val;
}
//对于一个改变映射到所有行上
}
int main() {
int n,m,p;
scanf("%d %d %d",&n,&m,&p);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int val;scanf("%d",&val);
inject(i,j,i,j,val);
}
}

while(p--){
int lx,ly,rx,ry,val;
scanf("%d %d %d %d %d",&lx,&ly,&rx,&ry,&val);
inject(lx,ly,rx,ry,val);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
record[i][j] += record[i][j-1];
printf("%d ",record[i][j]);
}
printf("\n");
}
}
/*

3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <iostream>

using namespace std;

const int N = 1010;

int n, m, q;
int a[N][N], b[N][N];

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;

/*
我们将b[i][j]中的值定义为影响
那么b[i][j]的值意义为 从第i行到最后一个行 从第j列到最后一列所有的范围内的元素全部增加b[i][j]的值
那么我们增加一个范围的定义为
1111111111111111111
1111112111111411111
1111111111111111111
1111111111111111111
1111111111113111111
1111114111111511111
1111111111111111111

那么 2 3点的范围即为 2的范围 在两个4的点进行截至但是
4的截至会对5的范围产生两次故需要恢复5的截至
那么就可以得到影响代码 我们插入一个元素本质上是只在2点和三点相同时增加的val值为目标元素


*/



}

int main()
{
scanf("%d%d%d", &n, &m, &q);

for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
scanf("%d", &a[i][j]);

for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
insert(i, j, i, j, a[i][j]);

while (q -- )
{
int x1, y1, x2, y2, c;
cin >> x1 >> y1 >> x2 >> y2 >> c;
insert(x1, y1, x2, y2, c);
}

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];

/*
这里是指对于影响的整合
1102111111111111111
1123111111111111111
1111111111111111111
1111111111111111111
1111111111111111111
1111111111111111111
1111111111111111111
对于3点的影响范围来说 相当于 3的值和两个2的值的累加但是对于两个2的影响来说他们都收到0的影响
对于0的影响发生了重合 故需要减去 那么[0,0][3.x,3.y]所有的影响就会汇总到3点上其他的数值不回对三点造成影响那么3点的数值就可以认为成3点呗处理后的数值进行输出了
ps:通过调试代码证明了本质上二维数组是一维概念
*/


for (int i = 1; i <= n; i ++ )
{
for (int j = 1; j <= m; j ++ ) printf("%d ", b[i][j]);
puts("");
}

return 0;
}