基础算法笔记2
前缀和
定义——前缀和可以简单理解为【数列的前n项和】,是一种重要的预处理方式,能大大降低查询的时间复杂度。
一维前缀和模板
S[i] = a[1] + a[2] + ... a[i]
a[l] + ... + a[r] = S[r] - S[l - 1]
例题
有N 个的正整数放到数组 A里,现在要求一个新的数组B,新数组的第 i个数 B[i]是原数组 A第0到第i个数的和。
#include <iostream>
using namespace std;
int N, A[10000], B[10000];
int main() {
cin >> N;
for (int i = 0; i < N; i++)
{ cin >> A[i]; } // 前缀和数组的第一项和原数组的第一项是相等的。
B[0] = A[0];
for (int i = 1; i < N; i++)
{ // 前缀和数组的第 i 项 = 原数组的 0 到 i-1 项的和 + 原数组的第 i 项。 B[i] = B[i - 1] + A[i]; }
for (int i = 0; i < N; i++)
{ cout << B[i] << " "; }
return 0;}
二维前缀和模板
S[i, j] = 第i行j列格子左上部分所有元素的和
以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]
模板题
子矩阵的和
#include<iostream>
using namespace std;
#define N 1010
int a[N][N];
int s[N][N];
int main()
{
int n, m, q;
int i, j;
cin >> n >> m >> q;
for(i = 1; i <= n; i++)
for(j = 1; j <= m; j++)
{
scanf("%d",&a[i][j]);
s[i][j] = s[i - 1][j] + s[i][j - 1] + a[i][j] - s[i - 1][j - 1];
}
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;
}
差分
定义——差分是一种和前缀和相对的策略,可以当做是求和的逆运算。
一维差分模板
给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c
差分(模板题)
#include<iostream>
using namespace std;
#define N 100010
int a[N]; //前缀和
int b[N]; //差分
void insert(int l, int r, int c)
{
b[l] += c;
b[r + 1] -= c;
}
int main()
{
int n, m;
int i;
scanf("%d%d",&n, &m);
for(i = 1; i <= n; i++)
{
scanf("%d",&a[i]);
insert(i, i, a[i]);
}
while(m--)
{
int l, r, c;
cin >> l >> r >> c;
insert(l, r, c);
}
for(i = 1; i <= n; i++)
{
b[i] +=b[i - 1]; //等价于 a[i] = a[i - 1] + b[i],输出 a[i] 就行
cout << b[i] << ' ';
}
cout << endl;
return 0;
}
二维差分模板
数组的首元素为0,不用它,因为 b[1][1] 需用到 b[0][0] .
给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c:
S[x1, y1] += c, S[x2 + 1, y1] -= c, S[x1, y2 + 1] -= c, S[x2 + 1, y2 + 1] += c
差分矩阵(模板题)
#include <iostream>
using namespace std;
#define N 1010
int a[N][N]; //前缀和
int b[N][N]; //差分
void insert(int x1, int y1, int x2, int y2, int c)
{
b[x1][y1] += c;
b[x1][y2 + 1] -= c;
b[x2 + 1][y1] -= c;
b[x2 + 1][y2 + 1] += c;
}
int main()
{
int n, m, q;
int i, j;
scanf("%d%d%d",&n, &m, &q);
for(i = 1; i <= n; i++)
for(j = 1; j <= m; j++)
{
scanf("%d",&a[i][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(i = 1; i <= n; i++)
{
for(j = 1; j <= m; j++)
{
b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1];
cout << b[i][j] << ' ';
}
cout << endl;
}
return 0;
}
刷题get的小知识:同余定理:数论中的重要概念。给定一个正整数m,如果两个整数a和b满足a-b能够被m整除,即(a-b)/m得到一个整数,那么就称整数a与b对模m同余,记作a≡b(mod m)。对模m同余是整数的一个等价关系。
出自洛谷P3131 [USACO16JAN] Subsequences Summing to Sevens S