算法基础:一维差分

差分http://www.acwing.com/problem/content/799/

类似数学中的求导和积分,差分可以看成前缀和的逆运算
首先给定一个原数组a:a[1],a[2],a[3]...a[n];
然后我们构造一个数组b:b[1],b[2],b[3],...b[i];
使得a[i]=b[1]+b[2]+...+b[i];
也就是说,a数组是b数组的前缀和数组,反过来我们把b数组叫做a数组的差分数组。换句话说,每一个a[i]都是b数组中从头开始的一段区间和。
考虑如何构造差分数组b?
如下:

差分数组的构造
a[0] = 0;
b[1] = a[1] - a[0];
b[2] = a[2] - a[1];
b[3] = a[3] - a[2];
......
b[n-1] = a[n] - a[n-1];

只要有了b数组,通过前缀和运算,就可以在O(n)的时间内得到a数组。
那么差分数组有什么用呢?
有这样一个问题:
给定区间[l,r],让我们把a数组中的[l,r]区间中的每一个数都加上c,即a[l]+c,a[l+1]+c,a[l+2]+c...+a[r]+c;
暴力做法是for循环lr区间,时间复杂度O(n),如果我们需要对原数组执行m次这样的操作,就会变成O(n * m)更高效的做法是可以考虑差分做法。
明确,a数组是b数组的前缀和数组。b数组的b[i]修改,会影响到a数组中从a[i]及往后的每一个数
首先让差分b数组中的b[l]+c,a数组变成a[l]+c,a[l+1]+c,a[l+2]+c...a[n]+c;
然而我们只要求lr区间加上c,因此还要对差分b数组进行操作。
然后让差分b数组b[r+1]-c,a数组变成a[r+1]-c,a[r+2]-c,...a[n]-c;

综上,得出一维差分的结论:给a数组中的[l,r]区间中的每一个数都加上c,只需对差分数组b b[l] += c, b[r+1] -= c。时间复杂度为O(1), 大大提高了效率。

#include <bits/stdc++.h>

using namespace std;

const int N = 100010;
int a[N], b[N]; //a数组是b数组的前缀和数组
int main(){
	int n, m;
	cin >> n >> m;
	
	for(int i = 1; i <= n; i++){  //构造差分数组 从1开始读入,避免出现a[-1]的情况
		cin >> a[i];
		b[i] = a[i] - a[i - 1];
	}
	
	int l, r, c;
	while(m--){
		cin >> l >> r >> c;
		b[l] += c;
		b[r + 1] -= c;
	}
	
	for(int i = 1; i <= n; i++){  //前缀和运算
		a[i] = a[i - 1] + b[i];
		cout << a[i] << " ";
	}

	
	return 0;
}

posted @ 2022-11-24 22:24  csai_H  阅读(170)  评论(0)    收藏  举报