前缀和和差分数组

首先知道前缀和概念是什么,假设有一个数列\(A\),他的前缀和数列为\(S\)

那么很容易就能得到前缀和公式:\(S[i]=\displaystyle\sum^i_{j=1}A[j]\)

如果要求区间\([l,r]\)的和呢?

答案也是很简单:\(sum[l,r]=S[r]-S[l-1]\)

所以前缀和用来求区间和问题非常有用。


如果一道题要让你对多个不同区间\([l,r]\)进行加减操作,再让你输出某一段区间的和呢?如果直接暴力大概率TLE,所以接下来要引入差分数组的概念:

设原数组为\(A[i]\),差分数组为\(B[i]\),则:

\[B[i]=\begin{cases} A[i]&{i=1}\\A[i]-A[i-1]&{i\geq2} \end{cases}
\]

差分数组的性质:

  • 如果对区间\([l,r]\)进行修改,对差分数组的修改就只有\(,B[l],B[r+1]\)\(B[l]\)加上修改值,\(B[r+1]\)减去修改值)
  • \(A[i]=\displaystyle\sum^i_{j=1}B[j]\) (可以通过\(B[i]=A[i]-A[i-1]\)来证明)
  • \(S[x]=\displaystyle\sum^x_{i=1}A[i]=\displaystyle\sum^x_{i=1}\displaystyle\sum^i_{j=1}B[j]=\displaystyle\sum^x_{i=1}(x-i+1)*B[i]\)

所以可以通过差分数组来计算前缀和。


下面上道例题:

有n个数。

m个操作,每一次操作,将x~y区间的所有数增加z;

最后有q个询问,每一次询问求出x~y的区间和。

直接用暴力前缀和很明显没办法快速计算,所以得用到差分数组。

\(A\)为原数组。

先求出差分数组\(B[i]=A[i]-A[i-1]\)

再根据\(m\)个操作修改\(B[i]\)

  • 设每一项\(A[i]=f[i]=f[i-1]+B[i]\)
  • 则前缀和\(S[i]=S[i-1]+f[i]\)

(上面两步为差分数组性质3倒数第二步,也可以直接通过差分数组性质3最后一步直接计算)

最后\(sum[x,y]=sum[y]-sum[x-1]\)


接下来是一道二维的前缀和题目,也是运用了一点DP

最大子矩阵

\(dp[i,j]\)保存的是\(dp[1,1]\)\(dp[i,j]\)的矩形内的所有元素的和,即前缀和。

#include<bits/stdc++.h>
using namespace std;

int dp[1005][1005];

int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		memset(dp, 0, sizeof(dp));
		int m, n, x, y, mmax = 0;
		cin >> m >> n >> x >> y;
		for(int i=1;i<=m;i++)
			for (int j = 1; j <= n; j++)
			{
				cin >> dp[i][j];
				dp[i][j] += dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1];//二维数组前缀和
				if (i >= x && j >= y)
					mmax = max(mmax, dp[i][j] - dp[i - x][j] - dp[i][j - y] + dp[i - x][j - y]);//求x*y内元素的最大和
			}
		cout << mmax << endl;
	}
	return 0;
}

原文链接: https://www.cnblogs.com/JMWan233/p/11179953.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    前缀和和差分数组

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/298859

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月15日 下午8:10
下一篇 2023年2月15日 下午8:11

相关推荐