前缀和与差分
前缀和
一、定义
前缀和是一种重要的预处理,能大大降低查询的时间复杂度。可以简单理解为“数列的前n项的和”。
二、公式
1、一维前缀和:
B[0] = A[0];
for (int i = 1; i < n; i++) {
B[i] = B[i - 1] + A[i];
}
2、二维前缀和:
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j];
}
}
三、经典例题
最大连续子序列
题目:
给定一个整数序列
A
1
,
A
2
,
…
…
,
A
N
A_1,A_2,……,A_N
A1,A2,……,AN,请你计算长度为 K 的最大连续子序列。
注意:这里的长度为 K,表示连续子序列的元素个数为 K,这里的最大是指 K 个元素的和最大。
分析:
此题可以在输入完后进行前缀和处理,然后用循环从k - 1到n - 1枚举看i与i - k前缀和之差的最大值。
代码
#include <cstdio>
#include <algorithm>
using namespace std;
long long a[1000005], b[1000005];
int main() {
long long n, k, x, y, maxn = -2147483647;
scanf("%lld %lld", &n, &k);
for(int i = 0; i < n; i++) scanf("%lld", &a[i]);
b[0] = a[0];
for(int i = 1; i < n; i++) {
b[i] = b[i - 1] + a[i];
}
for(int i = k - 1; i < n; i++) {
long long tot = b[i] - b[i - k];
maxn = max(tot, maxn);
}
printf("%lld", maxn);
return 0;
}
差分
一、定义
每个元素与前一个元素的差值,多用于区间一次修改求和。
二、公式
1、一维差分
a[1] = b[1];
for(int i = 2; i <= n; i++) {
b[i] = a[i] – a[i - 1];
}
2、区间修改[l,r]增tot:
b[l] + add , b[r+1] – add;
再做前缀和即可
三、经典题目
叠干草
题目:
有 N (为奇数)堆干草,按 1…N 编号,开始时每堆高度都是 0。
FJ给出 K 条指令,每条指令包含两个用空格隔开的整数,例如 “10 13 ”,表示给 10,11,12,13 这四堆干草分别叠加一捆干草,即高度均增加1。
FJ想知道,干草对完后,这 N 堆干草高度是多少。
分析:
此题通过差分的区间更改值解决。在输入时进行差分处理,输入区间时更改区间,最后做前缀和,输出。
代码:
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
int a[1000005], b[1000005], sum[1000005];
int main() {
int n, m, l, r, x;
scanf("%d %d", &n, &m);
b[1] = a[1];
for(int i = 2; i <= n; i++) {
b[i] = a[i] - a[i - 1];
}
for(int i = 1; i <= m; i++) {
scanf("%d %d", &l, &r);
b[l] += 1;
b[r + 1] -= 1;
}
for(int i = 1; i <= n; i++) {
sum[i] = sum[i - 1] + b[i];
}
for (int i = 1; i <= n; i++) {
printf("%d", sum[i]);
}
return 0;
}