差分

1|0本质思想

构造一个 b 数组, 满足 a 数组是 b 数组的前缀和。
差分是前缀和的逆运算。

2|0P2367 语文成绩

P2367 语文成绩 - 洛谷

暴力模拟过不了,时间复杂度是 O(n2)

2|1差分思想

  • 对于数组 a,定义 a 的差分数组为 b,其中 b1=a1,bi=aiai1(2in)
i 1 2 3 4 5 6 7 8
ai 1 1 2 2 1 3 3 5
bi 1 0 1 0 -1 2 0 2
  • 对数组 b 求前缀和:

    i=1nbi=a1+i=2n(aiai1)=i=1naii=1n1ai=an

  • 可以发现

    • 若将 bx 增加 1,再对 b 数组求前缀和得到 a 数组,那么得到的 a 数组中 ax,ax+1,,an 均增加 1

    • 若将 by 减少 1,再对 b 数组求前缀和得到 a 数组,那么得到的 a 数组中 ay,ay+1,,an 均减少 1

    • 例如,对于一个全为 0 的差分数组 b,若将 b3 增加 1,将 b6 减少 1,得到的 a 数组如表所示:

      i 1 2 3 4 5 6 7 8
      bi 0 0 +1 0 0 -1 0 0
      ai 0 0 1 1 1 0 0 0
  • 因此,将 a 数组中第 x 元素到第 y 元素增加 z 的修改操作,可以转化为将 bx 增加 zby+1 减少 z,最后再对 b 数组求前缀和。

  • 这样,对于每次修改操作,只需要修改 2 个点,时间复杂度变为 O(1)

  • 同时,因为只有在最后一次修改操作才需要得到 a 数组的具体值,所以只需要所有修改操作结束后求一次前缀和即可。

2|2代码实现

#include<iostream> #include<algorithm> #include<cstdio> const int N = 5e6 + 10; int n, m, x, y, z, a[N], b[N]; int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr); std::cin >> n >> m; for (int i = 1; i <= n; i++) std::cin >> a[i], b[i] = a[i] - a[i - 1]; for (int i = 1; i <= m; i++) { std::cin >> x >> y >> z; b[x] += z, b[y + 1] -= z; } int min = 0x7fffffff; for (int i = 1; i <= n; i++) { b[i] += b[i - 1]; min = std::min(min, b[i]); } std::cout << min; return 0; }

3|0P3397 地毯

P3397 地毯 - 洛谷

3|1差分思想

设数组 a 的差分数组为 b ,其中 ax,y=i=1xj=1ybi,j,用前缀和知识可知:

bx,y=ax,yax1,yax,y1+ax1,y1

截图20231211095101

b2,2 增加 1,再求二维前缀和,效果如图 ab

单独考虑一次覆盖操作,地毯左上角为 (x1,y1),右下角为 (x2,y2)。可以转化为:

  • bx1,y1,bx2+1,y2+1 增加 1
  • bx1,y2+1,bx2+1,y1 减少 1
  • 当地毯左上角为 (2,2) 右下角为 (3,3) 修改操作如图 cd

3|2代码实现

#include<iostream> #include<algorithm> #include<cstdio> const int N = 1e3 + 10; int n, m; int a[N][N]; int x1, y1, x2, y2; int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr); std::cin >> n >> m; for (int i = 1; i <= m; i++) { std::cin >> x1 >> y1 >> x2 >> y2; a[x1][y2 + 1]--, a[x2 + 1][y1]--; a[x1][y1]++, a[x2 + 1][y2 + 1]++; } for (int i = 1; i <= n; i++, std::cout << std::endl) { for (int j = 1; j <= n; j++) { a[i][j] += a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1]; std::cout << a[i][j] << " "; } } return 0; }

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/17893701.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(99)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示