前缀和(一维与二维) 差分
一维前缀和
很简单,可以联想等差数列求每一段和的公式 Sij = Sj - Si
一维的前缀和的初始化就是 S[i] = S[i-1] + a[i]
前缀和数组从1开始初始化
所以想要求 x -> y
段和 公式就是 res = S[y] - S[x-1]
代码
#include<iostream>
#include<cstdio>
using namespace std;
const int N = 100010;
int a[N], b[N];
int main()
{
int m, n;
scanf("%d%d", &m, &n);
for(int i = 1; i <= m; i++) scanf("%d", &a[i]);
for(int i = 1; i <= m; i++) b[i] = b[i-1]+a[i];
while(n--)
{
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", b[r] - b[l-1]);
}
system("pause");
return 0;
}
二维前缀和
典型例题就是求矩阵中子矩阵的和
S[i][j]
就是从矩阵左上角到右下角i j的子矩阵和
根据几何:看图
初始化的公式就是: s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j];
求子矩阵公式: res = s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1]
这两个公式可以互推
代码:
#include <iostream>
using namespace std;
const int N = 1010;
int n, m, q;
int a[N][N], s[N][N];
int main()
{
cin >> n >> m >> q;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
cin >> a[i][j];
}
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
s[i][j] = a[i][j] + s[i-1][j] + s[i][j-1] - s[i-1][j-1];
}
}
while(q--)
{
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
int res = s[x2][y2] - s[x2][y1-1] - s[x1-1][y2] + s[x1-1][y1-1];
cout << res << endl;
}
system("pause");
return 0;
}
关于坐标为什么要减一
这里的意思是我们直接把小格作为坐标而不是交点,看图,求图中绿色子矩阵的和
一维差分
差分就是求前缀和的逆运算,假设对数组b是a的差分数组 那么a就是b的前缀和数组
作用:主要作用就是想要在a数组里给定 [l, r]
区间里各加一个数,我们可以用一个循环,用O(n)
完成 但是有了差分后可以用O(1)
完成这个工作
假设b是a的差分数组 那么 b[i]+c
,那么 a[i]~a[n]
全部都会加c,因为a相当于b的前缀和数组 所以利用这个性质 我们只需要操作两个数就可以达到这个作用
b[l] += c ;
b[r] -= c ;
看图:
对于b数组的构造我们不用去管 我们可以假设一开始a b 两个数组都为0,那么输入的过程我们就可以认为是对b做了n次插入操作,第一次就是, 第二次, ....那么每往a中插一个数字 就相当于往b中差一个数字 所以我们只需遍历一遍a 插入到b中即可 所以我们的 insert 可以通用
最后再对b求一遍前缀和 就是我们的答案
代码:
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 100010;
int n, m;
int a[N], b[N];
void insert(int l, int r, int c)
{
b[l] += c;
b[r+1] -= c;
}
//总体思路就是先差分 之后再求前缀和
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i++) insert(i, i, a[i]); //第一步就是假设两个a b数组都为0,然后可以认为输入的每个数据都是现插入的
//这样就可以直接构造原来数组的差分数组
while(m--)
{
int l, r, c;
scanf("%d%d%d", &l, &r, &c);
insert(l, r, c);
}
for(int i = 1; i <= n; i++) b[i] += b[i-1];
for(int i = 1; i <= n; i++) printf("%d ", b[i]);
system("pause");
return 0;
}
二维差分
构造:类比于一维
核心代码:
b[x1][y1] += c;
b[x2+1][y1] -= c;
b[x1][y2+1] -= c;
b[x2+1][y2+1] += c;
看图:
代码:
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 1010;
int n, m, q;
int a[N][N], b[N][N];
void insert(int x1, int y1, int x2, int y2, int c)
{
b[x1][y1] += c;
b[x2+1][y1] -= c;
b[x1][y2+1] -= c;
b[x2+1][y2+1] += c;
}
int main()
{
scanf("%d%d%d", &n, &m, &q);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
scanf("%d", &a[i][j]);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; 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(int i = 1; i <= n; i++)
for(int j = 1; j<= m; j++)
b[i][j] += b[i-1][j] + b[i][j-1] - b[i-1][j-1];
for(int i = 1; i <= n; i++)
{
for(int j = 1; j<= m; j++)
{
cout << b[i][j] << " ";
}
cout << endl;
}
system("pause");
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端