例 输入一个长度为n的整数序列。接下来再输入m个询问,每个询问输入一对l, r。对于每个询问,输出原序列中从第l个数到第r个数的和。如果暴力遍历,时间复杂度为O(m*n) 可能超时
则采用前缀和算法并查询
a[N]记得是从1开始,a[0]=0, 因为如果是求(1,r) 需要sum[r]-sum[0]

[前缀和] [c++]
1
2
3
4
5
6
7
8
9
10
11
const int N = 1e5 + 10;
int sum[N], a[N]; //sum[i]=a[1]+a[2]+a[3].....a[i];
for(int i = 1;i <= n; i++)
{
sum[i] = sum[i - 1] + a[i]; //前缀和的初始化
}
while(m--)
{
cin>>l>>r;
cout<<sum[r] - sum[l - 1];
}

输入一个n行m列的整数矩阵,再输入q个询问,每个询问包含四个整数x1, y1, x2, y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。
二维前缀和思路相同 但是要推导公式

[二维前缀和] [c++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int n, m, q;
int a[N][N];s[N][M];
int main()
{
cin>>n>>m>>q;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
cin>>a[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]+a[i][j]; //前缀和的初始化
while (q -- )
{
int x1, y1, x2, y2;
cin>>x1>>y1>>x2>>y2;
cout<<s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1])<<endl; //算子矩阵的和
}
return 0;
}

一维差分类似于数学中的求导和积分,差分可以看成前缀和的逆运算
首先给定一个原数组a:a[1], a[2], a[3],,,,,, a[n];
然后我们构造一个数组b : b[1], b[2], b[3],,,,,, b[i];
使得 a[i] = b[1] + b[2] + b[3] + ,,,,,, + b[i]
也就是说,a数组是b数组的前缀和数组,反过来我们把b数组叫做a数组的差分数组。


输入一个长度为n的整数序列。
接下来输入m个操作,每个操作包含三个整数l, r, c,表示将序列中[l, r]之间的每个数加上c。
请你输出进行完所有操作后的序列。

[一维差分] [c++]
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 = 1e5 + 10;
int a[N],b[N],n,m;
void insert(int l,int r,int c)
{
b[l]+=c;
b[r+1]-=c;
}

int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) insert(i,i,a[i]);
while(m--)
{
int l,r,c;
cin>>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++) cout<<b[i]<<" ";
return 0;
}

二维思路相似

[二维差分] [c++]
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
#include<iostream>
#include<cstdio>
using namespace std;
const int N = 1e3 + 10;
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;
}
int main()
{
int n, m, q;
cin >> n >> m >> q;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> 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]; //二维前缀和
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
printf("%d ", b[i][j]);
}
printf("\n");
}
return 0;
}

acwing 3729
这道题一开始想的区间合并 听了讲解发现居然能差分
因为每个位置的数只会增加 所以只要操作次数不是0 那么值就为1
怎么记录操作次数呢 就需要用差分
b[1]到b[i]的前缀和就是第i个元素的操作次数 极大节省时间
差分数组不用刻意去构造 创建的时候就能直接弄出来

[3729] [c++]
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;
long long n,mid;
const int N=2e5+10;
long long b[N];
void add(int l,int r)
{
b[l]+=1;
b[r+1]-=1;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
long long t;
cin>>t;

while(t--)
{
int n;
cin>>n;
memset(b,0,sizeof b);
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
if(x==0) continue;
else if(x>=i) add(1,i);
else add(i-x+1,i);
}
for(int i=1;i<=n;i++) b[i]+=b[i-1]; //此时b[i]存的是操作次数

for(int i=1;i<=n;i++)
{
if(b[i]!=0) cout<<1<<" ";
else cout<<0<<" ";
}
cout<<endl;

}

return 0;
}