前言;
前缀和与差分算法在算法竞赛中是一个基础类算法,常用来解决一些区间询问类的问题,同时也存在一些扩展形式,例如异或和。
必须掌握
1. 一维前缀和与差分
2. 二维前缀和与差分
3. 异或和
一维前缀和
前缀和的主要作用是快速求解数组区间的和。如果我们需要频繁地查询某个子区间的和,直接计算会非常慢,因为我们每次都要遍历这个区间。而利用前缀和,我们就能迅速得到答案。
用数组维护前缀和
我们定义前缀数组 sum[]
,sum[x]
表示 𝑎1∼ax 的和值,即 𝑠𝑢𝑚𝑥= 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;
}