前缀和与差分

话说有个喜欢信息学竞赛的小白,今天刷题的时候遇见一道自己怎么写就是过不了的题

 乍一看这不就是先把每个数存在一维数组里,每次询问就暴力地做一次循环这么简单的事而已。

 教练呵呵一笑 

  

他再仔细看题,这最多可能有10000个数,1000次询问;时间复杂度太高了!

教练就交给他了一种又快又帅的算法-------一维前缀和与差分!!!!!!!!

一维前缀和和差分

预处理

简单介绍一下,前缀和是一种重要的预处理,能大大降低查询的时间复杂度。可以看做动态规划的一种。简单理解就是“数列的前N项和”,差分即是作差,是以前缀和预处理为基础的。可以说两者是相辅相成的关系。

这个优化主要是用来在O(1)时间内求出一个序列中a[i]+a[i+1]+……+a[j]的和

预处理的转移方程给出       f[i+1]=f[i]+输入的数

f[x] 表示输入的第一个数到第x个数的和

可能还是不明不白,没挂系。且听我给你解释

为了求连续几项的和,必须先进行预处理(也就是前缀和)

每当输入第x个数,那么从第一个到加上现在这个数的和就等于输入前x-1个数加上第x个数

比如说有一组数据   1 3 2 9 6 那么当输入第五个数,求前五个数的和(1+3+2+9+6)就等于前四个数的和(1+3+2+9)加上第五个数(6)

下一步就是用代码实现这个过程

for(int i=1;i<=n;i++)
{
    int k;
    cin>>k;
    f[i]=f[i-1]+k;
}

 每次输入一个数k,进行预处理

差分

下一步就是计算a[i]+a[i+1]+a[i+2]+.......+a[j]   (a数组为存储输入的数据的数组)

用到的原理——容斥原理

即a[1]+a[2]+......+a[j-1]+a[j]-(a[1]+a[2]+.....+a[i-2]+a[i-1])=a[i]+a[i+1]+a[i+2]+.......+a[j]

∵ a[1]+a[2]+......+a[j-1]+a[j]=f[j]     且a[1]+a[2]+.....+a[i-2]+a[i-1]=f[i-1]

∴a[1]+a[2]+......+a[j-1]+a[j]-(a[1]+a[2]+.....+a[i-2]+a[i-1])=f[j]-f[i-1]=a[i]+a[i+1]+a[i+2]+.......+a[j]

给出本题参考代码

#include<bits/stdc++.h>
using namespace std;
	int T;
	int j;
	long long a[1000000]; 
	long long b[1000000]; 
int main()
{
	cin>>T;

	for(int i=1;i<=T;i++)
	{
		int n,m;
		cin>>n>>m;
		for(int i=1;i<=n;i++)
		{
			int k;
			cin>>k;
			a[i]=a[i-1]+k;
		}
		for(int i=1;i<=m;i++)
		{
			int x,y;
			cin>>x>>y;
			j++;
			b[j]=a[y]-a[x-1];
		}
	}
	for(int i=1;i<=j;i++)
	{
		cout<<b[i]<<endl;
	}
	return 0;
} 

“谢谢教练”

又过了一段时间,小白已经不是小白了,正在准备CSP的复赛。

今天教练让全班做了模拟测试,小白做到第二题时,一眼就发现这是一道一位前缀和,他欣喜若狂,马上就将代码打了出来,并且最后拿了100。可惜第三题又把它给难住了

于是他决定用四重循环枚举(也是厉害) 

然后

 他又去问教练。

于是教练又传授了他一招——二维前缀和与差分!!!!!!

二维前缀和与差分

预处理

定义一个二维f数组存储从f[1][1]到输入的数所在坐标的前缀和(即矩形内所有元素的和)

同样是利用容斥原理

用a数组存储输入的数

就用上文题目的样例数据说明

假设现在输入[2,3]的数5,就要计算从[1,1]到[2,3]围成矩形的和(这是为了后面在循环枚举各个矩形和是能通过简单的加减算出答案,而不是每次进行循环枚举每在矩形里的数现场求和。这样能极大的节约时间)

即sum([2,3])=sum([2,2])+sum([1,3])-sum([1,1])

∵sum([2,2])=f[2][2]      sum([1,3])=f[1][3]   sum([1,1])=f[1][1]

∴sum([2,3])=f[2][3]=f[2][2]+f[1][3]-f[1][1]

推出 f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]

与处理完后,得到任意一点到a[1][1]围成矩形里所有数之和

差分

在本题中求得是面积一定下数之和最大

同样是容斥原理

只不过现在的面积不是一,二是一个待求的矩形;

相信聪明的你一定可以自己画图理解其中的道理,这里直接给出转移方程

假设矩形长r,宽q

则有 面积和=f[i][j]-f[i-r][j]-f[i][j-q]+f[i-r][j-q]

下面给出本题参考代码

#include<bits/stdc++.h>
using namespace std;
int n,m,r,c;
int t;
int f[1001][1001];
int a[1001][1001];
int main()
{
	cin>>n>>m>>r>>c;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			int x;
			cin>>Map[i][j];
			f[i][j]=a[i][j]+f[i-1][j]+f[i][j-1]-f[i-1][j-1];
		}
	}
	for(int i=1;i<=n-r+1;i++)
	{
		for(int j=1;j<=m-c+1;j++)
		{
			t=max(t,f[i+r-1][j+c-1]+f[i-1][j-1]-f[i-1][j+c-1]-f[i+r-1][j-1]);
		}
	}
	cout<<t;
}

  总结

1.一维前缀和预处理转移方程    f[i+1]=f[i]+输入的数

   一维前缀差分转移方程           sum=f[j]-f[i-1]

2.二维前缀和预处理转移方程    f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]

  二维前缀差分转移方程            面积和=f[i][j]-f[i-r][j]-f[i][j-q]+f[i-r][j-q]

3.差分的实质——容斥原理

practice makes perfect    熟 方 能 生 巧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值