前缀和:
前缀和可以理解为某个序列前n项的和,不限于一维还是二维。对于一个数列an,前i项的前缀和Si = a1 + a2 + a3 + … + ai
差分:
差分可以理解为前缀和的逆运算,对于一个序列an,对于任意相邻的两项ai和a(i+1),二者之差即|ai-a(i+1)|即为此数列在i处的差分。
前缀和的应用。
例一:一维前缀和
常规思路:对与任意的l与r,遍历al到ar进行求和操作,然后输出结果。时间复杂度:O(m*(r-l))最坏情况O(n²)
前缀和思路:对序列进行前缀和求和,对于任意的l到r之间的和,都可表示为sr - s(i-1)
利用前缀和的思想,可以将O(n²)的复杂度的算法优化为O(n+m)的算法。
代码如下:
#include<iostream>
using namespace std;
const int MAXN=1e5+7;
int a[MAXN];
int s[MAXN];
int main()
{
int n,m;
cin>>n>>m;
s[0]=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];//输入序列的值
s[i]=s[i-1]+a[i];//进行前缀和求和
}
for(int i=1;i<=m;i++)//进行查询操作
{
int x,y;
cin>>x>>y;
cout<<s[y]-s[x-1]<<endl;//前缀和作差求区间和
}
return 0;
}
例二:二维前缀和
朴素算法思路:对于一个二维矩阵,要知道矩阵上面每一个RxR正方形内的权值之和,进行for循环枚举,遍历每一个满足条件的正方形的解并计算权值和,时间复杂度:O(n²RR)
前缀和思路:可以利用二维前缀和来求解,对于坐标aij,前缀和Sij表示从a11到aij围成的矩形的权和。
假设蓝色区域为最优区域为我们所求,由图所示以及包含排斥定理和容斥定理可知,S = Sij - Si(j-R) - S(i-R)j + S(i-R)(j-R)
#include<iostream>
#include<cstring>
using namespace std;
const int MAXN=5005;
int r,n;
int f[MAXN][MAXN];
int w[MAXN];
int main()
{
memset(f,0,sizeof(f));
cin>>n>>r;
for(int i=1;i<=n;i++)
{
int x,y,z;
cin>>x>>y>>z;
f[x+1][y+1]+=z;
}
for(int i=1;i<=5001;i++)
{
for(int j=1;j<=5001;j++)
{
f[i][j]+=f[i-1][j]+f[i][j-1]-f[i-1][j-1];
}
}
int maxn=0;
for(int i=r;i<=5001;i++)//必须都从r开始,否则会出现a[负数]
{
for(int j=r;j<=5001;j++)
{
if(maxn<f[i][j]-f[i-r][j]-f[i][j-r]+f[i-r][j-r])
maxn=f[i][j]-f[i-r][j]-f[i][j-r]+f[i-r][j-r];
}
}
cout<<maxn<<endl;
return 0;
}
差分:对于一个序列an,它的差分bn=an-a(n-1)
差分的应用:
例三:一维差分
思路说明:
要想让每一个数都相等,则需要满足差分bn全部为0。即b2、b3、、bn均为0
按照差分的区间操作原理(如上图),对区间进行+1或者-1,差分序列一增一减。故按照贪心思想,让正减小负增加,一正一负配对。全部匹配完之后剩余的可以跟b1或者bn+1进行增减匹配,因为b1对整个差分序列没有影响。
因此最少操作次数为:min(正数的和,负数的和)+abs(正数的和-负数的和)
最终的到的结果数为:abs(正数的和-负数的和)+1
解释最终得到的结果数(上一行)。当剩余的数都对b1进行加减差分时,b1变化abs(正数的和-负数的和),当剩余的数都对bn+1进行加减差分时,b1变化为0,故b1一共有abs(正数的和-负数的和)+1种答案
代码如下:
#include<iostream>
#include<cmath>
using namespace std;
const int MAXN=1e5+7;
int a[MAXN];
int b[MAXN];
int main()
{
int n;
cin>>n;
a[0]=0;
long long int zheng=0,fu=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
b[i]=a[i]-a[i-1];
}
for(int i=2;i<=n;i++)
if(b[i]>0)
zheng+=b[i];
else
fu-=b[i];
cout<<min(zheng,fu)+abs(zheng-fu)<<endl;
cout<<abs(zheng-fu)+1<<endl;
}
例四:一维差分
思路:每两头牛可以互相看到对方,则说明两头牛之间的牛都比这两头牛矮,而已知了只有一头牛的身高,所以可以起初假设所有牛一样高,然后两头牛都能看到对方,则将两头牛之间的牛的身高-1,算出相对的身高差分,最后差分求和即可。
代码如下:
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
const int MAXN = 1e4+7;
int c[MAXN],d[MAXN];
bool point[MAXN][MAXN];
int main()
{
int n,p,h,m;
memset(c,0,sizeof(c));
cin>>n>>p>>h>>m;
for(int i=1;i<=m;i++)
{
int x,y;
cin>>x>>y;
if(x>y)
swap(x,y);
if(point[x][y]==false)
c[x+1]--,c[y]++,point[x][y]=true;
}
for(int i=1;i<=n;i++)
d[i]=d[i-1]+c[i];
for(int i=1;i<=n;i++)
cout<<h+d[i]<<endl;
}