前置知识:前缀和(一维、二维)_如何何何的博客-CSDN博客
差分算法可以在 O(1) 的时间复杂度下对一个集合中一的个连续子集有中的所元素做一个相同操作,假设有数组 q、s,s 为 q 的前缀和数组,则 q 为 s 的差分数组。
一维差分:
在 O(1) 的时间复杂度下,对序列的一个连续子序列内的元素做一个相同的操作。
对于一个数字序列,有一个操作 (l , r , c) ,意思是将序列 l~r 内的元素加上 c,求经过操作后的序列;
将原序列 s 看做前缀和序列,构造出其差分序列 q;
要使得 s序列 l~r 内的元素加上 c ,即 q[l] += c,q[r+1] -= c ,再对 q 做求前缀和操作,求出的序列就是操作过后的序列。
代码模板如下:
for(int i=1;i<=n;i++)//构建差分数组
a[i]=s[i]-s[i-1];
while(m--){//l~r加上c
q[l]+=c;
q[r+1]-=c;
}
for(int i=1;i<=n;i++)//前缀和操作
s[i]=s[i-1]+q[i];
二维差分:
在 O(1) 的时间复杂度下,对矩阵的子矩阵中的元素做一个相同操作。
对于一个数字矩阵,有一个操作 (m , n , o , p , c) ,意思是将以 (m , n) 为左上角,(o , p)为右下角的子矩阵内的所有元素加上 c ,求经过操作后的矩阵;
和一维差分相同,先构造原矩阵 s 的差分矩阵 q ;
以(m,n)为左上角,(o,p)为右下角的二维子序列中的所有元素加上c,即 q[m][n] += c,q[m][p+1] -= c,q[o+1][n ]-= c,q[o+1][p+1] += c;
最后对q序列做前缀和操作,得到操作后的序列。
图示:
代码模板如下:
for (int i = 1; i <= n; i++)//构造差分矩阵
for (int j = 1; j <= m; j++)
q[i][j] = s[i][j] - s[i - 1][j] - s[i][j - 1] + s[i - 1][j - 1];
while (k--) {//(a,b)~(c,d)加上x
q[a][b] += x;
q[a][d + 1] -= x;
q[c + 1][b] -= x;
q[c + 1][d + 1] += x;
}
for (int i = 1; i <= n; i++) //还原成前缀和矩阵
for (int j = 1; j <= m; j++)
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + q[i][j];
例题1. IncDec序列:
思路:
要使得序列所有元素相等,即其差分数组除了第一项之外的所有项为0;
先构造出原序列s的差分数组q,差分数组的一次操作有两种,只改变一个数或者一个数加一个数减;
假设差分数组中的负数项之和为 n ,正数项之和为 m ,先同时操作两个数,待一项被抵消完后,再考虑另一项,待差分数组所有项全为 0 时序列中所有元素相等,则最小操作数为 max( -n , m );
得到的序列不同在与 n 和 m 其中一项被抵消之后对另一项的操作方式,剩下的一项可以单独操作,也可以与第一项一起同时操作,使得相同序列共有 abs(n + m) + 1 种。
AC代码如下:
#include<iostream>
#include<cmath>
using namespace std;
const int N = 1e5+10;
typedef long long LL;
LL q[N],s[N];
int main(){
int n;
cin>>n;
LL l=0,r=0;
for(int i=1;i<=n;i++){
cin>>s[i];
q[i]=s[i]-s[i-1];
if(q[i]>0&&i>=2)l+=q[i];//大于0
else if(q[i]<0&&i>=2)r+=q[i];//小于0
}
cout<<max(l,-r)<<endl;
cout<<abs(l+r)+1;
return 0;
}