前缀与差分
Posted spciay
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前缀与差分相关的知识,希望对你有一定的参考价值。
前缀与差分
1. 算法分析
1.1 前缀和
定义
s[n] = (sum_{i=1}^na[i])
递推关系
s[i] = a[i] + s[i - 1]
区间求和
(sum_{i=l}^ra[i] = s[r] - s[l - 1])
1.2 差分
定义
存在两个数组a(a1, a2, a3,..., an)和b(b1, b2, ... ,bn)
如果ai = b1 + b2 + ... + bi
那么b称为a的差分(比如: b1 = a1, b2 = a2 - a1)
作用
- 区间增加->单点修改:当a[l]~a[r]这个区间内元素全加上c时,只需要对b[l] + c, b[r+1] - c即可,因为:b[l] + c:让a[l]往后的元素全部加上c。b[r+1]-c:防止a[r+1]开始往后的元素加上c
- 一开始给ai赋值时,可以看成是给a[i]~a[i]这段全部加上c
构造
差分的构造可以选择: b[i] = a[i] - a[i - 1]
变形
差分可以维护a的变化值,即b[i] = Δai = $ sum_{i=1}^nb[i] $
2. 板子
2.1 前缀和
2.1.1 一维前缀和
#include <bits/stdc++.h>
using namespace std;
int const N = 1e5 + 10;
int s[N], a[N];
int main()
{
int n, k;
cin >> n >> k;
// 计算前缀和
for (int i = 1; i <= n ; ++i)
{
scanf("%d", &a[i]);
s[i] = s[i - 1] + a[i];
}
// 使用前缀和
for (int i = 0; i < k ; ++i)
{
int l, r;
scanf("%d %d", &l, &r);
printf("%d
", s[r] - s[l -1]);
}
return 0;
}
2.1.2 二维前缀和
#include <bits/stdc++.h>
using namespace std;
int const N = 1e3 + 10;
int s[N][N], a[N][N];
int main()
{
int n, m, k;
cin >> n >> m >> k;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
{
scanf("%d", &a[i][j]);
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j]; // 前缀和在二维的情况
}
for (int i = 0; i < k; ++i)
{
int l1, r1, l2, r2;
scanf("%d %d %d %d", &l1 ,&r1, &l2, &r2);
printf("%d
", s[l2][r2] - s[l2][r1 - 1] - s[l1 - 1][r2] + s[l1 - 1][r1 - 1]); // 打印结果
}
return 0;
}
2.2 差分
2.2.1 一维差分
#include <bits/stdc++.h>
using namespace std;
int const N = 1e5 + 10;
int b[N], n, m, a[N];
// 区间修改
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) scanf("%d", &a[i]); // 读入原数组
for (int i = 1, l, r, c; i <= m; ++i) { // 不断读入区间增加
scanf("%d %d %d", &l, &r, &c);
insert(l, r, c);
}
for (int i = 1; i <= n; ++i) {
b[i] += b[i - 1]; // 计算a[i]的变化值
printf("%d ", a[i] + b[i]);
}
return 0;
}
2.2.1 二维差分
#include <bits/stdc++.h>
using namespace std;
int const N = 1e3 + 10;
int a[N][N], b[N][N];
int n, m, q;
// 区间增加(二维情况)--和前缀和情况不一样
void insert(int l1, int r1, int l2, int r2, int c)
{
b[l1][r1] += c;
b[l2 + 1][r2 + 1] += c;
b[l2 + 1][r1] -= c;
b[l1][r2 + 1] -= c;
}
int main() {
cin >> 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, l1, r1, l2, r2, c; i <= q; ++i) {
scanf("%d%d%d%d%d", &l1, &r1, &l2, &r2, &c); // 区间增加
insert(l1, r1, l2, r2, 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]; // 前缀求和,计算变化量
printf("%d ", a[i][j] + b[i][j]); // 计算变化后的a[i][j]
}
printf("
");
}
return 0;
}
以上是关于前缀与差分的主要内容,如果未能解决你的问题,请参考以下文章