知识点

前缀和

  • 一维
    • S[i] = a[1] + a[2] + … a[i]
    • a[l] + … + a[r] = S[r] - S[l - 1]
  • 二维

    • 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]

      差分

  • 一维

    • 差分数列B定义为:B[1] = A[1],B[i] = A[i] - A[i - 1], 2 <= i <= n
    • 给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c
  • 二维

image.png

  • 给以(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

    例题

    前缀和

    激光炸弹

    直接从左上到右下递推出整个前缀和,然后枚举出最大值
    需要注意,目标在地图上的位置可能重合,因此是累加而不是直接赋值
    炸弹的范围可能非常大,因此需要缩小枚举的空间到地图最大范围(右下角) ```cpp

    include

using namespace std;

const int N = 5010;

int s[N][N];

int main() { int n, r; cin >> n >> r;

  1. r = min(r, 5001);
  2. int a = 0, b = 0;
  3. while (n--) {
  4. int x, y, w;
  5. scanf("%d%d%d", &x, &y, &w);
  6. x++, y++;
  7. a = max(a, x), b = max(b, y);
  8. s[x][y] += w;
  9. }
  10. a = max(a, r), b = max(b, r);
  11. for (int i = 1; i <= a; i++)
  12. for (int j = 1; j <= b; j++)
  13. s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
  14. int res = 0;
  15. for (int i = r; i <= a; i++)
  16. for (int j = r; j <= b; j++)
  17. res = max(res, s[i][j] - s[i - r][j] - s[i][j - r] + s[i - r][j - r]);
  18. cout << res;
  19. return 0;

}

  1. <a name="Fl42b"></a>
  2. ## 差分
  3. <a name="ESKRQ"></a>
  4. ### [增减序列](https://www.acwing.com/problem/content/102/)
  5. 记差分序列中正数总和为p,负数的绝对值总和为q<br />差分是区间边界上一个加一个减,因此首先要尽可能选择差分序列中一正一负的两个点,共操作min(p, q)次<br />只剩下正的或者是负的的时候,记这个位置为a,可以有两种选择:[0, a - 1]和[a, n],共操作|p - q|次,对应|p - q| + 1种不同结果
  6. ```cpp
  7. #include <iostream>
  8. using namespace std;
  9. typedef long long LL;
  10. const int N = 1e5 + 10;
  11. int a[N];
  12. int main() {
  13. int n;
  14. cin >> n;
  15. for (int i = 1; i <= n; i++) {
  16. scanf("%d", &a[i]);
  17. }
  18. LL pos = 0, neg = 0;
  19. for (int i = 2; i <= n; i++) {
  20. int d = a[i] - a[i - 1];
  21. if (d > 0) {
  22. pos += d;
  23. } else if (d < 0) {
  24. neg += -d;
  25. }
  26. }
  27. cout << max(pos, neg) << endl << abs(pos - neg) + 1;
  28. return 0;
  29. }

最高的牛

每头牛身高尽可能高,那就先假设每头牛的身高和最高的那头牛一样
然后对于每一对关系,相当于两个端点的区间内牛的身高至少要减1,可以用差分来做
最后求一遍前缀和就得到了每头牛的身高
注意可能存在重复的关系
在unordered_set里面不能直接使用pair

  1. #include <iostream>
  2. #include <set>
  3. using namespace std;
  4. typedef pair<int, int> PII;
  5. const int N = 1e4 + 10;
  6. int h[N];
  7. set<PII> exist;
  8. int main() {
  9. int n, p, H, m;
  10. cin >> n >> p >> H >> m;
  11. h[0] = H;
  12. for (int i = 1; i <= m; i++) {
  13. int a, b;
  14. scanf("%d%d", &a, &b);
  15. if (a > b) swap(a, b);
  16. if (!exist.count({a, b})) {
  17. exist.insert({a, b});
  18. h[a + 1]--;
  19. h[b]++;
  20. }
  21. }
  22. for (int i = 1; i <= n; i++) {
  23. h[i] += h[i - 1];
  24. printf("%d\n", h[i]);
  25. }
  26. return 0;
  27. }