【差分算法入门】例题讲解(代码逐句分析)

首先在介绍差分算法之前,读者需要了解什么是前缀和算法。前缀和算法是一种比较简单好用的算法,不过在这里就不详细讲解了。如果是对于前缀和算法还不了解的同学建议在学习完前缀和算法后再来学习差分算法。

首先我们要知道,对于一个原数组a[n],我们可以构造出一个前缀和数组b[n]用来求解数组a的区间总和。而差分数组则是构造一个数组c[n],使得原数组a变成了数组c的前缀和数组。这个操作有一点像在高数中对于一个原函数,前缀和是原函数的导数,而差分则类比于对原函数求微分。那么,我们求出差分数组c又有什么用呢?

情景引入 Acwing 差分

797. 差分


输入一个长度为 n 的整数序列。

接下来输入 m 个操作,每个操作包含三个整数 l,r,c,表示将序列中 [l,r] 之间的每个数加上 c。

请你输出进行完所有操作后的序列。

输入格式
第一行包含两个整数 n 和 m。

第二行包含 n 个整数,表示整数序列。

接下来 m 行,每行包含三个整数 l,r,c,表示一个操作。

输出格式
共一行,包含 n 个整数,表示最终序列。

数据范围
1≤n,m≤100000,
1≤l≤r≤n,
−1000≤c≤1000,
−1000≤整数序列中元素的值≤1000
输入样例:
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1
输出样例:
3 4 5 3 4 2

从这道题我们可以知道,如果我们要在区间 L~R 之间全部加上c,暴力的算法明显是会超时的,而有如果使用差分,我们只需要在c[L]上加上c,在c[R-1]上减去c,这样我们就可以把时间复杂度降为o(1)。就可以保证代码运行的速度。如果不信的话可以在草稿纸上推一遍。

核心a[i]=c[1]+c[2]+…+c[n]

上代码!!!!

#include<iostream>
#include<iomanip>
#include <utility>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<ctime>
#include<cstring>
#include<list>
#define ll long long
#define fi first
#define se second
using namespace std;
typedef  pair<int, int> PII;

const int N = 1e5 + 6;
int n, m;
int a[N];    //原函数
int b[N];    //差分函数 

/*在原函数没有赋值全为0的时候,差分函数自然也是全为0*/

void insert(int l, int r, int c)    //操作函数,把a[L]~a[R]全部加上c
{
	b[l] += c;
	b[r + 1] -= c;
}

void solve()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		insert(i, i, a[i]);  //这是对差分数组的初始化,把a[i]~a[i]赋值
	}
	while (m--)
	{
		int l, r, c;
		cin >> l >> r >> c;
		insert(l, r, c);
	}
	for (int i = 1; i <= n; i++)
	{
		a[i] = a[i-1] + b[i];    //前缀和计算
		cout << a[i] << ' ';
	}
}

int main()
{
	solve();
	return 0;
}

讲完了一维差分后,我们再来看看二维差分吧
情景引入Acwing差分矩阵

输入一个 n 行 m 列的整数矩阵,再输入 q 个操作,每个操作包含五个整数 x1,y1,x2,y2,c,其中 (x1,y1) 和 (x2,y2) 表示一个子矩阵的左上角坐标和右下角坐标。

每个操作都要将选中的子矩阵中的每个元素的值加上 c。

请你将进行完所有操作后的矩阵输出。

输入格式
第一行包含整数 n,m,q。

接下来 n 行,每行包含 m 个整数,表示整数矩阵。

接下来 q 行,每行包含 5 个整数 x1,y1,x2,y2,c,表示一个操作。

输出格式
共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。

数据范围
1≤n,m≤1000,
1≤q≤100000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤c≤1000,
−1000≤矩阵内元素的值≤1000
输入样例:
3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1
输出样例:
2 3 4 1
4 3 4 1
2 2 2 2

对于二维差分而言,思路其实和一维差分的差别不是很大,如果我们需要对于a[x1][y1],a[x2][y2]为对角线的矩形进行加c操作,我们只需要进行如下操作就可以了。

	//cf[i][j]是a[i][j]的差分矩阵
    cf[x1][y1] += c;
	cf[x1][y2 + 1] -= c;
	cf[x2+1][y1] -= c;
	cf[x2+1][y2 + 1] += c;

如果对于上面代码不能理解可以在草稿纸上画个图

ac代码

#include<iostream>

#define ll long long

using namespace std;


ll n, m, q;

ll cf[1005][1005];

ll insert(ll x1, ll y1, ll x2, ll y2,ll c)
{
	cf[x1][y1] += c;
	cf[x1][y2 + 1] -= c;
	cf[x2+1][y1] -= c;
	cf[x2+1][y2 + 1] += c;
}

void solve()
{
	cin >> n >> m >> q;
	for (ll i = 1; i <= n; i++)
	{
		for (ll j = 1; j <= m; j++)
		{
			ll t;
			cin >> t;
			insert(i, j, i, j, t);
		}
	}
	while (q--)
	{
		ll a, b, c, d, e;
		cin >> a>> b>>c>>d>>e;
		insert(a, b, c, d, e);
	}
	for (ll i = 1; i <= n; i++)
	{
		for (ll j = 1; j <= m; j++)
		{
			cf[i][j] += cf[i - 1][j] + cf[i][j - 1]-cf[i-1][j-1];
		}
	}
	for (ll i = 1; i <= n; i++)
	{
		for (ll j = 1; j <= m; j++)
		{
			cout << cf[i][j] << ' ';
		}
		cout <<  endl;
	}
}

int main()
{
	solve();
	return 0;
}


作者:Avalon·Demerzel

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值