前缀和
定义
假定有一数组
a
[
1
]
~
a
[
n
]
a[1] \sim a[n]
a[1]~a[n],前缀和
s
[
i
]
s[i]
s[i]就是前i 项的和。 例如
s
[
1
]
=
a
[
1
]
s[1] = a[1]
s[1]=a[1]
s
[
2
]
=
a
[
1
]
+
a
[
2
]
s[2] = a[1] + a[2]
s[2]=a[1]+a[2]
s
[
n
]
=
a
[
1
]
+
a
[
2
]
+
.
.
.
.
.
.
+
a
[
n
]
s[n] = a[1] + a[2] + ...... + a[n]
s[n]=a[1]+a[2]+......+a[n]
应用
由此我们可以得到,若要求得数组
a
[
l
]
~
a
[
r
]
a[l] \sim a[r]
a[l]~a[r]项的元素总和,只需要前r 项的元素总和s[r] 减去前l-1 项的元素总和s[l - 1] 即可。
模板
s[i] = a[1] + a[2] + a[3] + ...... + a[i];
a[l] + a[l + 1] + ... + a[r] = s[r] - s[l - 1];
例题
题目描述
输入一个长度为 n 的整数序列。
接下来再输入 m 个询问,每个询问输入一对 l,r。
对于每个询问,输出原序列中从第 l 个数到第 r 个数的和。
输入格式
第一行包含两个整数n 和m 。
第二行包含n 个整数,表示整数数列。
接下来m 行,每行包含两个整数l 和r ,表示一个询问的区间范围。
输出格式
共 m 行,每行输出一个询问的结果。
数据范围
1
≤
l
≤
r
≤
n
1≤l≤r≤n
1≤l≤r≤n,
1
≤
n
,
m
≤
100000
1≤n,m≤100000
1≤n,m≤100000,
?
1000
≤
数
列
中
元
素
的
值
≤
1000
?1000≤数列中元素的值≤1000
?1000≤数列中元素的值≤1000
输入样例:
5 3
2 1 3 6 4
1 2
1 3
2 4
输出样例:
3
6
10
题解
对于每个询问的l 和r ,都需要返回序列的区间
a
[
l
]
~
a
[
r
]
a[l] \sim a[r]
a[l]~a[r]之和,根据模板很容易得出我们需要的代码
#include <iostream>
using namespace std;
const int N = 100010;
int n, m;
int s[N];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> s[i], s[i] += s[i - 1];
while (m--) {
int l, r;
cin >> l >> r;
cout << s[r] - s[l - 1] << endl;
}
return 0;
}
注意事项
前缀和的数组从
a
[
1
]
a[1]
a[1]开始是因为
s
[
i
]
=
s
[
i
?
1
]
+
a
[
i
]
s[i] = s[i -1] + a[i]
s[i]=s[i?1]+a[i] 假如从
a
[
0
]
a[0]
a[0]开始的话
s
[
0
]
=
s
[
?
1
]
+
a
[
0
]
s[0] = s[-1] + a[0]
s[0]=s[?1]+a[0]显然有问题。
差分
定义
假定有一数组
a
[
1
]
~
a
[
n
]
a[1] \sim a[n]
a[1]~a[n],此时我们设一个数组
b
[
1
]
~
b
[
n
]
b[1] \sim b[n]
b[1]~b[n],使得
a
[
i
]
=
b
[
1
]
+
b
[
2
]
+
.
.
.
+
b
[
i
]
a[i] = b[1] + b[2] + ... + b[i]
a[i]=b[1]+b[2]+...+b[i] 因此我们容易得出
b
[
1
]
=
a
[
1
]
b[1] = a[1]
b[1]=a[1]
b
[
2
]
=
a
[
2
]
?
a
[
1
]
b[2] = a[2] - a[1]
b[2]=a[2]?a[1]
b
[
n
]
=
a
[
n
]
?
a
[
n
?
1
]
b[n] = a[n] - a[n - 1]
b[n]=a[n]?a[n?1] 由此我们也可以发现差分就是前缀和的逆运算。
应用
差分可以解决一个区间[l,r] 内同时加上或者减去同一个数的问题。 具体实现如下: 要实现a[l] ~ a[r] 同时加上一个数c ,我们首先从差分的定义入手 a[l - 1] = b[1] + b[2] + ... + b[l - 1] a[l] = b[1] + b[2] + ... + b[l - 1] + b[l] a[r] = b[1] + b[2] + ... + b[l - 1] + b[l] + b[l + 1] + ... + b[r] a[r + 1] = b[1] + b[2] + ... + b[l - 1] + b[l] + b[l + 1] + ... + b[r] + b[r + 1] 从上面式子中我们可以发现a[l - 1] 以后的元素都有b[l] ,而a[r + 1] 以前的元素都没有b[r + 1] , 因此要在a[l] ~ a[r] 同时加上c ,只要让b[l] + c ,b[r + 1] - c 即可。
模板
给区间[l, r]中的每个数加上c:
B[l] += c, B[r + 1] -= c
例题
题目描述
输入一个长度为n 的整数序列。
接下来输入m 个操作,每个操作包含三个整数 l ,r ,c ,表示将序列中 [l,r] 之间的每个数加上 c 。
请你输出进行完所有操作后的序列。
输入格式
第一行包含两个整数 n 和 m。
第二行包含 n 个整数,表示整数序列。
接下来 m 行,每行包含三个整数 l,r,c,表示一个操作。
输出格式
共一行,包含 n 个整数,表示最终序列。
数据范围
1
≤
n
,
m
≤
100000
1≤n,m≤100000
1≤n,m≤100000,
1
≤
l
≤
r
≤
n
1≤l≤r≤n
1≤l≤r≤n,
?
1000
≤
c
≤
1000
?1000≤c≤1000
?1000≤c≤1000,
?
1000
≤
整
数
序
列
中
元
素
的
值
≤
1000
?1000≤整数序列中元素的值≤1000
?1000≤整数序列中元素的值≤1000
输入样例:
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1
输出样例:
3 4 5 3 4 2
题解
有了上面的分析,这题只要把b[1] ~ b[n] 求出来,再套用模板即可AC。
#include<iostream>
using namespace std;
const int N = 1e5+10;
int a[N],b[N];
int main(){
int n,m,tmp;
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++){
scanf("%d",&a[i]);
b[i] = a[i] - a[i - 1];
}
while(m --){
int l,r,c;
scanf("%d%d%d",&l,&r,&c);
b[l] += c;
b[r + 1] -= c;
}
int k = 0;
for(int i = 1; i <= n; i++){
k += b[i];
printf("%d ",k);
}
return 0;
}
|