前缀和:用来快速求区间的和
输入一个长度为n的整数序列。
接下来再输入m个询问,每个询问输入一对l, r。
对于每个询问,输出原序列中从第l个数到第r个数的和。
1≤l≤r≤n,
1≤n,m≤100000,
−1000≤数列中元素的值≤1000
我们如果每次都把l到r区间的数加起来,那么在数据最大的情况下,时间复杂度就达到了O(1e10+),这样我们显然是不能接受的
这时候就用到的前缀和思想
我们定义一个数组a用来存放输入的元素,然后再定义一个数组存放前i个元素的和(包括第i个元素),那么,如果我们求l到r区间的和,我们直接求s[r]-s[l-1]就可以,这样表示前r个元素的和减去前l-1个元素的和,这样就得到闭区间l到r的和,这样的时间复杂度是O(m+n)
#include<iostream>
using namespace std;
const int N=1e5+5;
int a[N],n,q;
int main()
{
int l,r;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) scanf("%d",a+i),a[i]+=a[i-1];
while(q--)
{
scanf("%d%d",&l,&r);
printf("%d\n",a[r]-a[l-1]);
}
return 0;
}
一维的比较简单,二维的也话也是同样的思想,只不过是需要考虑的周到一些
子矩阵和
假定我们要求(i-1,j-1)到(i,j)围城矩阵的和,那么我们求出(0,0)到(i,j)的面积,然后减去除蓝色区域的面积即可,重点来了!!!
我们不可能分别求红色、浅蓝色、绿色三块的和,因为浅蓝色和绿色的和不好求,但是利用前缀和红色的和基本就是已知的,那么我们直接求红色+浅蓝色的和、红色+绿色的和即可,然后再减去多计算一次的红色的面积即可(容斥原理),这样的话时间复杂度为O(n*m+q)
#include<iostream>
using namespace std;
const int N=1e3+5;
int a[N][N],n,m,q,s[N][N];
int main()
{
int x1,y1,x2,y2;
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]),s[i][j]=s[i-1][j]+s[i][j-1]+a[i][j]-s[i-1][j-1];
while(q--)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
printf("%d\n",s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]);
}
return 0;
}