一维前缀和
前缀和的思想很常用,当我们需要对一段数列多次求其和的时候,就可以简少运算次数,使得O(n*n)的时间复杂度转化为O(n),前缀和有一个常用递归代码
for(int i=1;i<=n;i++){
s[i]=s[i]+s[i-1];
}
s[i]代表的是每一段从0加到i的和,所以要求一段子序列的和就会变得很简单
一维差分
差分被称为是前缀和的逆运算,因为差分求的是前一个数与后一个数的差,常用于要对一段序列进行增减的运算可以大大减少时间复杂度。
for(int i=1;i<=n;i++){
s[i]=s[i]-s[i-1];
}
二维前缀和
从为前缀和推导而来。如二维前缀和s[i][j]指的是从(1,1)到(i,j)的和;这里给一个例题洛谷p2004
#include<cstdio>
#include<iostream>
using namespace std;
const int N = 1e3 + 5;
long long q[N][N];
int main() {
int n, m, c;
cin >> n >> m >> c;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> q[i][j];
q[i][j] = q[i][j] + q[i - 1][j] + q[i][j - 1] - q[i - 1][j - 1];//求前缀和
}
}
int ans = -1999999;
int k, l;
for (int i = 1; i <= n ; i++) {
int x = i + c - 1;
if (x > n)break;
for (int j = 1; j <= m; j++) {
int y = j + c - 1;
if (y > m)break;
int aa = q[x][y] - q[i - 1][y] - q[x][j - 1] + q[i - 1][j - 1];//关键求一段
if (aa > ans) {
ans = aa;
k = i;
l = j;
}
}
}
cout << k << " " << l << endl;
return 0;
}
这里给个图理解一下
二维差分
同样的,是二维前缀和的反例,也同样给一道例题
#include<iostream>
#include<cstdio>
using namespace std;
int s[1000][1000] = { 0 };
int main() {
int n, m;
cin >> n >> m;
while (m--) {
int a, b, c, d;
cin >> a >> b >>c >> d;
s[a][b]++;
s[a][d + 1]--;
s[c + 1][b]--;
s[c + 1][d + 1]++;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
printf("%d ", s[i][j]);
}
printf("\n");
}
return 0;
}
洛谷p5542
小结
差分的值就等于自己-左边-上边+左上角 ,二维前缀和等于自己+左边+上边-左上角
而对俩坐标内的数据进行改变,则如图所示,左上角++,右下角++,左下边--,右上边--
而要求二维前缀和的差距,则和
图中所示。