【C++算法模板】预处理算法:一维差分、二维差分总结,详解带例题

复习一下前缀和吧:【C++算法模板】预处理算法:一维前缀和、二维前缀和总结,详解带例题-CSDN博客

0)概述

  • 差分的推导也比较简单,因此本博客重点在于知识点归纳而不在于证明

1)一维差分

  • 给定一个一维序列,定义它的差分序列 b b b 为,计算差分序列的时间复杂度为 O ( n ) O(n) O(n)
    • b [ 1 ] = a [ 1 ]   ( i = 1 ) b[1]=a[1]\ (i=1) b[1]=a[1] (i=1)
    • b [ i ] = a [ i ] − a [ i − 1 ]   ( 2 < = i < = n ) b[i]=a[i]-a[i-1]\ (2<=i<=n) b[i]=a[i]a[i1] (2<=i<=n)
  • 为原序列 [ l , r ] [l,r] [l,r] 区间分别加上 c c c 等价于: b [ l ] + = c ,   b [ r + 1 ] − = c b[l]+=c,\ b[r+1]-=c b[l]+=c, b[r+1]=c,对差分序列 b b b 做一维前缀和得到操作后的原序列,时间复杂度 O ( 1 ) O(1) O(1)

【例题】AcWing 797,链接:797. 差分 - AcWing题库

#include<bits/stdc++.h>
#define x first
#define y second

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;

// 题目描述: 

const int N=1e5+5;
int n,m;
int a[N],b[N]; // a:原序列,b:差分序列
int s[N]; // 对b求前缀和
int l,r,c;

int main() {
	cin>>n>>m;
	// 输入规模超过1e5,推荐使用scanf
	for(int i=1;i<=n;i++) {
		scanf("%d",&a[i]);	
		b[i]=a[i]-a[i-1]; // 前缀和和差分下标都从1开始,否则容易有边界问题
	}
	while(m--) {
		scanf("%d%d%d",&l,&r,&c);
		b[l]+=c;
		b[r+1]-=c;
	}
	// 用新数组s计算差分序列b的前缀和
	for(int i=1;i<=n;i++) {
		s[i]=s[i-1]+b[i];
		printf("%d ",s[i]);
	}
	cout<<endl;
	
	// 用累加的思想,在差分序列本身上做前缀和,节省空间
	for(int i=1;i<=n;i++) {
		b[i]+=b[i-1];
		printf("%d ",b[i]);
	}
	return 0;
}

2)二维差分

  • 在一维差分中我们对差分序列 b b b 求前缀和可以得到原序列 a a a,由此可以看出差分是前缀和的逆运算

在这里插入图片描述

  • 则我们可由二维前缀和计算公式轻松推导出二维差分序列的计算公式,时间复杂度 O ( n 2 ) O(n^2) O(n2)
    • 二维前缀和的计算公式 s [ i ] [ j ] = ∑ i = 1 i ∑ j = 1 j a [ i ] [ j ] = s [ i − 1 ] [ j ] + s [ i ] [ j − 1 ] − s [ i − 1 ] [ j − 1 ] + a [ i ] [ j ] s[i][j]=\sum_{i=1}^i\sum_{j=1}^ja[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j] s[i][j]=i=1ij=1ja[i][j]=s[i1][j]+s[i][j1]s[i1][j1]+a[i][j]
    • a [ i ] a[i] a[i] 单独移至等号一边,对 s s s 合并同类项,得到 a [ i ] [ j ] = s [ i ] [ j ] − s [ i − 1 ] [ j ] − s [ i ] [ j − 1 ] + s [ i − 1 ] [ j − 1 ] a[i][j]=s[i][j]-s[i-1][j]-s[i][j-1]+s[i-1][j-1] a[i][j]=s[i][j]s[i1][j]s[i][j1]+s[i1][j1]
    • 由于 b b b 的前缀和是 a a a,所以将 a a a 替换成 b b b,将 s s s 替换成 a a a,得到二位差分计算公式
    • b [ i ] [ j ] = a [ i ] [ j ] − a [ i − 1 ] [ j ] − a [ i ] [ j − 1 ] + a [ i − 1 ] [ j − 1 ] b[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1] b[i][j]=a[i][j]a[i1][j]a[i][j1]+a[i1][j1],其物理含义是:在下标 [ i ] [ j ] [i][j] [i][j] 位置上的差分等于原序列 [ i ] [ j ] [i][j] [i][j] 上的元素减去上边 [ i − 1 ] [ j ] [i-1][j] [i1][j] 和左边 [ i ] [ j − 1 ] [i][j-1] [i][j1] 上的元素并加上左上角 [ i − 1 ] [ j − 1 ] [i-1][j-1] [i1][j1] 的元素
  • 对原序列 a a a 区间 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) ( x 2 , y 2 ) (x_2,y_2) (x2,y2) 之间的元素加上 c c c,等价于其差分序列 b b b 的点 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) c c c,点 ( x 1 , y 2 + 1 ) (x_1,y_{2}+1) (x1,y2+1) c c c,点 ( x 2 + 1 , y 1 ) (x_2+1,y1) (x2+1,y1) c c c,点 ( x 2 + 1 , y 2 + 1 ) (x_2+1,y_2+1) (x2+1,y2+1) c c c,时间复杂度 O ( 1 ) O(1) O(1),最后对差分序列 b b b 求二维前缀和即可得到操作过后的原序列

在这里插入图片描述

【例题】AcWing 798,链接:798. 差分矩阵 - AcWing题库

#include<bits/stdc++.h>
#define x first
#define y second

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;

// 题目描述: 逐行差分和逐行求和的思路

const int N=1e3+5;
int n,m,k; // n:矩阵行数,m:矩阵列数,k:操作次数
int a[N][N];
int b[N][N]; // 差分矩阵

int s[N][N]; // 对b求二维前缀和得到操作后的原序列

int main() {
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++) {
			cin>>a[i][j];
			b[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1]; // 差分是前缀和的逆运算
		}
	}
	// 输出差分数组看一看
//	cout<<"输出差分数组:>\n";
//	for(int i=1;i<=n;i++,puts("")) {
//		for(int j=1;j<=m;j++) {
//			cout<<b[i][j]<<' ';
//		}
//	}
	int x1,y1,x2,y2,c;
	while(k--) {
		scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&c);
		b[x1][y1]+=c;
		b[x1][y2+1]-=c;
		b[x2+1][y1]-=c;
		b[x2+1][y2+1]+=c;
	}
	cout<<endl;
	// 求前缀和保存到s里面
	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]+b[i][j];
			cout<<s[i][j]<<' ';
		}
		cout<<endl;
	}
	cout<<endl;
	// 直接在b本身上进行前缀和得到操作后的a
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++) {
			b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
			cout<<b[i][j]<<' ';
		}
		cout<<endl;
	}
	cout<<endl;

	return 0;
}

相关推荐

  1. 基础算法--数组

    2024-03-18 08:28:01       51 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-18 08:28:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-18 08:28:01       101 阅读
  3. 在Django里面运行非项目文件

    2024-03-18 08:28:01       82 阅读
  4. Python语言-面向对象

    2024-03-18 08:28:01       91 阅读

热门阅读

  1. 程序分享--排序算法--桶排序

    2024-03-18 08:28:01       46 阅读
  2. 《C++ Primer Plus》第六章课后题

    2024-03-18 08:28:01       32 阅读
  3. Haproxy 负载均衡集群

    2024-03-18 08:28:01       41 阅读
  4. 【Docker】Solr容器化部署及配置参数详情

    2024-03-18 08:28:01       45 阅读
  5. Solr完结版

    2024-03-18 08:28:01       38 阅读
  6. Kafka(十)安全

    2024-03-18 08:28:01       34 阅读
  7. Mysql数据库的多实例部署

    2024-03-18 08:28:01       35 阅读
  8. widget一些控件的使用

    2024-03-18 08:28:01       41 阅读
  9. C++ L2【入门】求和

    2024-03-18 08:28:01       38 阅读