前缀和与差分

前言;

前缀和与差分算法在算法竞赛中是一个基础类算法,常用来解决一些区间询问类的问题,同时也存在一些扩展形式,例如异或和。

必须掌握

1.   一维前缀和与差分

2.  二维前缀和与差分

3.   异或和

一维前缀和

前缀和的主要作用是快速求解数组区间的和。如果我们需要频繁地查询某个子区间的和,直接计算会非常慢,因为我们每次都要遍历这个区间。而利用前缀和,我们就能迅速得到答案。

用数组维护前缀和

我们定义前缀数组 sum[]sum[x] 表示 𝑎1​∼ax​ 的和值,即 𝑠𝑢𝑚𝑥=\sum_{i=1}^{x} ax;

如果我们需要求 ar到al的和,我们可以利用前缀数组快速得到:

于是我们得到,对于求区间的和值,我们可以用两个前缀和做差得到,这个也叫做差分

如图所示:白色的一段由绿色的一段减去红色的一段。

一维前缀和模板
#include<bits/stdc++.h>
using namespace std;
const int N =1e4;
int main()
{
	int n;
	int a[N];
	cin>>n;
int	sum[N];//前缀和数组 
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		sum[i]=sum[i-1]+a[i] ;
	}//前缀和模板 
	return 0;
}
列题
大学里的数木要维护

#include <iostream>
using namespace std;
const int N=1e4;
int main()
{
    int n,m;
  int a[N],sum[N];
  cin>>n>>m;
  for(int i=1;i<=n;i++)
  {
    cin>>a[i];
    sum[i]=sum[i-1]+a[i];
  }
  while(m--)
  {
    int i,j;
    cin>>i>>j;
    cout<<sum[j]-sum[i-1]<<endl;
  }
  return 0;
}

二维前缀和

二维前缀和数组

一维的前缀和求的只是一排序列的和值,宏观上看,是一条线段、一段区间。但是二维的前缀合求的是一个矩形的和值,我们用图来表示,假设存在一个二维数组 𝑎𝑖,𝑗ai,j​,从 (1,1)(1,1) 到 (4,4)(4,4)。

我们定义前缀和数组 𝑠𝑢𝑚𝑖,𝑗sumi,j​ 为从 (1,1)∼(𝑖,𝑗)(1,1)∼(i,j) 的和值,例如下图:

我们利用一下容斥的思想,可以得到:𝑠𝑢𝑚(2,2)∼(3,3)=𝑠𝑢𝑚3,3−𝑠𝑢𝑚1,3−𝑠𝑢𝑚3,1+𝑠𝑢𝑚1,1sum(2,2)∼(3,3)​=sum3,3​−sum1,3​−sum3,1​+sum1,1​

二位前缀和数组的维护

我们用递推的思想,假设我们要求 𝑠𝑢𝑚(𝑛,𝑚)我们已经知道了 𝑠𝑢𝑚(𝑛−1,𝑚)那么sum(n,m)=sum(n-1,m)+a[n][1]+a[n][2]+.....a[n][m]

如图:

二维前缀和模板
#include<bits/stdc++.h>
using namespace  std;
const int N=1e6;
int main()
{
	int n,m;
	int sum[N][N];//前缀和数组
	int a[N][N]; 
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			cin>>a[i][j];
			sum[i][j]=sum[i][j-1]+a[i][j];   //一维前缀和维护 
		}
		for(int j=1;j<=m;j++)
		{
			sum[i][j]+=sum[i-1][j];    //二位前缀和维护 
		}
	}
	cout<<sum[2][2]<<endl<<sum[3][3]<<endl;
}
/*
    1 2 3 4
    5 6 7 8
    9 10 11 12
    13 14 15 16
*/ 

列题

二维前缀和

#include <iostream>
using namespace std;
const int N = 1e3+10;
using ll = long long;
ll sum[N][N];
ll a[N][N];
int n, m, q;
int main() {
  ll  ans;
    cin >> n >> m >> q;
   for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			cin>>a[i][j];
			sum[i][j]=sum[i][j-1]+a[i][j];   //一维前缀和维护 
		}
		for(int j=1;j<=m;j++)
		{
			sum[i][j]+=sum[i-1][j];    //二位前缀和维护 
		}
	}
    for (int i = 1; i <= q; ++i) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
      ans=sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1];
      cout<<ans<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值