上篇文章:从 O(N) 到 O(log N):LCR 173 点名问题的五种解法与最优推导
目录
1.【模板】前缀和

理解题意
此题下标从1开始,长度为n的数组,要创建n+1的大小才能访问到n这个数

算法原理
解法一:暴力解法->一一模拟
每次遍历从头到尾的和,O(n*q)
解法二:前缀和 -> 快速求出数组中某一个连续区间的和 ,O(1) -> O(q) + O(n)
第一步:预处理出来一个前缀和数组,用dp表示
dp[i]:表示[1,i]区间所有元素的和
dp[i] = dp[i - 1] + arr[i]

第二步:使用前缀和数组

[l , r] -> dp[r] - dp[l - 1]
细节问题
为什么下标要从1开始计数?
如果从0开始,那么dp[l-1] 就是 dp[-1]了,要处理边界问题
如果从1开始,dp[l-1] 就是 dp[0],此时,让dp[0] = 0即可。
在初始化时,添加虚拟结点(辅助结点)
代码
#include <iostream>
#include <vector>
using namespace std;
int main() {
// 1.读入数据
int n, q;
cin >> n >> q;
vector<int> arr(n + 1);
for(int i = 1; i <= n; i++)
{
cin >> arr[i];
}
// 2.预处理出一个前缀和数组
vector<long long> dp(n+1); // 求和可能超出范围,用long long
for(int i = 1; i <= n; i++)
{
dp[i] = dp[i - 1] + arr[i];
}
// 3. 使用前缀和数组
int l = 0, r = 0;
while(q--)
{
cin >> l >> r;
cout << dp[r] - dp[l - 1] << endl;
}
return 0;
}
2.【模板】二维前缀和


理解题意
第一行输入矩阵行数n,列数m,查询次数q(也算行数)
之后的矩阵行数(n),每行输入m个整数,且共计n * m个整数
再之后的q行,每行输入四个整数,且行数<=n,列数<=m
以示例举例并画图:

算法原理
预处理前缀和矩阵

使用前缀和矩阵

#include <iostream>
#include <vector>
using namespace std;
int main()
{
// 读入数据
int n = 0, m = 0, q = 0;
cin >> n >> m >> q;
vector<vector<int>> arr(n + 1, vector<int>(m + 1));
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
cin >> arr[i][j];
// 预处理前缀和矩阵
vector<vector<long long>> dp(n + 1, vector<long long>(m + 1));
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
dp[i][j] = dp[i-1][j] + dp[i][j-1] + arr[i][j] - dp[i-1][j-1];
// 使用前缀和矩阵
int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
while(q--)
{
cin >> x1 >> y1 >> x2 >> y2;
cout << dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1] << endl;
}
return 0;
}
本章完。