main
黄海 2 years ago
parent c1e6795d43
commit 382b37c11e

@ -0,0 +1,21 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
const int INF = 0x3f3f3f3f;
int n, a[N], s[N], ans[N];
int main() {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i], s[i] = s[i - 1] + a[i]; // 前缀和
int mi = 0;
for (int i = 1; i <= n; i++) {
ans[i] = s[i] - mi;
mi = min(mi, s[i]);
}
int res = -INF;
for (int i = 1; i <= n; i++) res = max(res, ans[i]);
cout << res << endl;
return 0;
}

@ -0,0 +1,28 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int b[N][N], s[N][N];
int n, m;
int main() {
cin >> n >> m;
while (m--) {
// 从0开始构建差分数组
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
b[x1][y1] += 1; // 进行子矩阵的加减,差分
b[x2 + 1][y1] -= 1;
b[x1][y2 + 1] -= 1;
b[x2 + 1][y2 + 1] += 1;
}
// 还原为原始数组
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
s[i][j] = b[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1]; // 把之前的加减结果进行求和
printf("%d ", s[i][j]); // 注意输出格式,每个数带一个空格
}
printf("\n"); // 结束一行的输出输出一个换行符号
}
return 0;
}

@ -0,0 +1,103 @@
## 前缀和和差分洛谷题单总结
[参考文献](https://blog.csdn.net/piqihaoshaonian/article/details/127515000)
### 一、公式
#### 前缀和的公式
一维:$s[i] = a[i] + s[i-1]$
二维:$s[i][j] = a[i][j] + s[i-1] [j] + s[ i] [j-1] - s[i-1][j-1]$
#### 差分的公式
一维:$b[i] =s[i] - s[i-1]$
二维:$b[i] = s[i][j] - s[i-1][j]-s[i][j-1]+s[i-1][j-1]$
### 二、题单
#### [$P1115$ 最大子段和](https://www.luogu.com.cn/problem/P1115)
![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202312170827397.png)
**分析**
先求每个位置的前缀和(某个区间求和前缀和可以说是最快的),然后去找该位置前前缀和的最小值,如果要求一段和最大,就要用这段和减去前面最小的值。
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
const int INF = 0x3f3f3f3f;
int n, a[N], s[N], ans[N];
int main() {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i], s[i] = s[i - 1] + a[i]; // 前缀和
int mi = 0;
for (int i = 1; i <= n; i++) {
ans[i] = s[i] - mi;
mi = min(mi, s[i]);
}
int res = -INF;
for (int i = 1; i <= n; i++) res = max(res, ans[i]);
cout << res << endl;
return 0;
}
```
#### [$P3397$ 地毯](https://www.luogu.com.cn/problem/P3397)
![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202312170847131.png)
**分析**
看到这里的时候,我就想到了一个矩阵的某个子矩阵进行加减,瞬间想到二维差分和二位前缀和,二位差分的公式为:
![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202312170848375.png)
由差分算的二位前缀和公式:
![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202312170849629.png)
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int b[N][N], s[N][N];
int n, m;
int main() {
cin >> n >> m;
while (m--) {
// 从0开始构建差分数组
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
b[x1][y1] += 1; // 进行子矩阵的加减,差分
b[x2 + 1][y1] -= 1;
b[x1][y2 + 1] -= 1;
b[x2 + 1][y2 + 1] += 1;
}
// 还原为原始数组
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
s[i][j] = b[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1]; // 把之前的加减结果进行求和
printf("%d ", s[i][j]); // 注意输出格式,每个数带一个空格
}
printf("\n"); // 结束一行的输出输出一个换行符号
}
return 0;
}
```
### [$P3406$ 海底高铁](https://www.luogu.com.cn/problem/P3406)
![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202312170857701.png)
**分析**
① 每一段的最小费用加起来则总体费用最小。
② 这里的区间是线段而不是一个具体的数,所以我们需要以一个统一的标准进行区间段的区分:于是我们想到了以每个区间的左端点值进行整个线段的记录。
③ 节约时间可以对某段区间做同样的加减数的方法:想到的就是差分(当然有差分就会有由差分求前缀和)。
④ 最后用得到的线段数比较两种购买方案。
**注意**
当然代码中还有很多需要记录的细节!!!例如:线段数是站点数-1同时差分和前缀和的循环最好是从1开始涉及边界问题
Loading…
Cancel
Save