一维/二维_差分/前缀和_代码实现

一维前缀和

代码实现:

  • 计算公式:S[r]-S[l-1]=l~r范围和
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <stdio.h>

using namespace std;

const int N=100010;

int n, m;					//n表示数组已存入的数个数,m表示需要查询的次数
int A[N], S[N];

int main()
{

	scanf("%d%d", &n, &m);

	for (int i = 1; i < n; i++)scanf("%d", &A[i]);

	for (int i = 1; i < n; i++)S[i] = S[i - 1] + A[i];

	while (m--)
	{
		int l, r;
		scanf("%d%d", &l, &r);				//区间和的计算
		printf("%d\n",S[r] - S[l - 1]);
	}
	system("pause");
	return 0;
}

一维前缀和(2)

//计算一维前缀和(q次询问,输入l和r,输出l->r之间的和值)
//公式:sum[l->r]=sum[r]-sum[l-1];sum[i]=a[i]+sum[i-1];

void test01()
{
	int a[1000] = { 0 }, sum[1000] = { 0 }; sum[0] = 0;
	int n, q, l, r;
    
	cin >> n >> q;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		sum[i] = sum[i - 1] + a[i];
	}
	while (q--) {
		cin >> l >> r;
		printf("%d", sum[r] - sum[l - 1]);
	}
}

二维前缀和

代码实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
const int N = 1010;

int n, m, q;
int a[N][N], s[N][N];

int main()
{
	scanf("%d%d%d", &n, &m, &q);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			scanf("%d", &a[i][j]);

	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] + a[i][j];		//求前缀和

	while (q--)
	{
		int x1, y1, x2, y2;
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);	
		printf("%d\n", s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]);		//求子矩阵
	}
	return 0;
}

二维前缀和(2)

//计算二维前缀和		
//前缀和数组公式:sum[x][y] = sum[x - 1][y] + sum[x][y - 1] - sum[x - 1][y - 1] + a[x][y];
//子矩阵	    :sum[x_2-x_1][y_2-y_1]=sum[x_2][y_2]-sum[x_2][y_1-1]-sum[x_1-1][y_2]+sum[x_1-1][y_1-1];
//例题:求区间[x_1,y_1]->[x_2,y_2]区间中数值之和
void test03()
{
	int a[50][50] = { 0 }, sum[50][50] = { 0 };
	int n, m, q;			//n行m列,q次询问

	cin >> n >> m >> q;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
		{
			cin >> a[i][j];
			sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j];
		}
	while (q--)
	{
		int x_1, y_1, x_2, y_2;
		cin >> x_1 >> y_1 >> x_2 >> y_2;
		cout << sum[x_2][y_2] - sum[x_1 - 1][y_2] - sum[x_2][y_1 - 1] + sum[x_1 - 1][y_1 - 1] << endl;
	}
}

一维差分

  • 原理:有一数列{a1,a2,a3…an}构造{b1,b2,b3…bn}使得an=b1+b2+…+bn.
  • 代码实现:
给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c

#include <iostream>
using namespace std;

const int N = 10010;

int n, m;
int a[N], b[N];

void insert(int l, int r, int c)
{
	b[l] += c;
	b[r + 1] -= c;
}
int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++)scanf("%d", &a[i]);
	for (int i = 1; i <= n; i++)insert(i, i, a[i]);
	while (m--)
	{
		int l, r, c;
		scanf("%d%d%d", &l, &r, &c);
		insert(l, r, c);
	}
	for (int i = 1; i <= n; i++)b[i] += b[i - 1];
	for (int i = 1; i <= n; i++)printf("%d", b[i]);
	system("pause");
	return 0;
}

一维差分(2)

//计算一维差分
//例题:在n个数中,进行q次操作,每次询问一个区间[l->r],使得区间中所有数加上一个数值c
//由已知数值序列A{a_1,a_2,a_3.....a_n}构造差分序列D{b_1,b_2,b_3.....b_n}使得a_n=b_1+b_2+.....+b_n;4
//公式:A[L->R]中每个数值+c    <=>    D[L]+c,D[R+1]-c
int  D[1000] = { 0 };
void insert(int l, int r, int c) { D[l] += c, D[r + 1] -= c; }
void test02()
{
	int A[1000] = { 0 };
	int n, q, l, r, c;

	cin >> n >> q;
	for (int i = 1; i <= n; i++)//创建数值序列A并构造A的差分序列D
	{
		cin >> A[i]; 
		insert(i, i, A[i]);
	}
	while (q--)
	{
		cin >> l >> r >> c;
		insert(l, r, c);
	}
	for (int i = 1; i <= n; i++)
	{
		D[i] += D[i - 1];
		cout << D[i] << " ";
	}//将差分数组D做一次前缀求和就行
}

二维差分

代码实现:

给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c:
S[x1, y1] += c, S[x2 + 1, y1] -= c, S[x1, y2 + 1] -= c, S[x2 + 1, y2 + 1] += c
    
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;

const int N = 1010;

int n, m, q;
int a[N][N], b[N][N];

void insert(int x1, int y1, int x2, int y2, int c)
{
	b[x1][y1] += c;
	b[x1][y2 + 1] -= c;
	b[x2 + 1][y1] -= c;
	b[x2 + 1][y2 + 1] += c;
}

int main()
{
	scanf("%d%d%d", &n, &m, &q);

	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			scanf("%d", &a[i][j]);

	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			insert(i, j, i, j, a[i][j]);

	while (q--)
	{
		int x1, y1, x2, y2, c;
		cin >> x1 >> y1 >> x2 >> y2 >> c;
		insert(x1, y1, x2, y2, c);
	}
	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];	

	for (int i = 1; i <= n; i++)
		for (int j = 1; j < m; j++)
			printf("%d  ", b[i][j]);

	return 0;
}

二维差分(2)

//计算二维差分
/* 例题:在一个 n* m 的矩阵中进行q次操作,将(x_1, y_1)->(x_2, y_2)中的元素都加上数值c
		 即给以(x_1, y_1)为左上角,(x_2, y_2)为右下角的子矩阵中的所有元素加上c          */
//由已知矩阵创建差分矩阵,原矩阵每个对应元素A(i,j)的值都等于以差分矩阵D(0,0)为起点D(i,j)为终点的子矩阵所有元素的值求和
//公式:D[x1][y1] += c; D[x1][y2 + 1] -= c; D[x2 + 1][y1] -= c; D[x2 + 1][y2 + 1] += c;
int D[100][100];
void insert(int x_1, int y_1, int x_2, int y_2,int c) 
{ 
	D[x_1][y_1] += c;
	D[x_1][y_2 + 1] -= c;
	D[x_2 + 1][y_1] -= c;
	D[x_2 + 1][y_2 + 1] += c;
}
void test04()
{
	int n, m, q;
	int A[100][100] = { 0 };
	cin >> n >> m >> q;
	for(int x=1;x<=n;x++)
		for (int y = 1; y <= m; y++)
		{
			cin >> A[x][y];
			insert(x, y, x, y, A[x][y]);
		}
	while (q--)
	{
		int x_1, x_2, y_1, y_2, c;
		cin >> x_1 >> y_1 >> x_2 >> y_2 >> c;
		insert(x_1, y_1, x_2, y_2, c);
	}
	for (int x = 1; x <= n; x++)
	{
		for (int y = 1; y <= m; y++)
		{
			D[x][y] += D[x - 1][y] + D[x][y - 1] - D[x - 1][y - 1];
			cout << D[x][y] << " ";
		}
		cout << endl;
	}
	return;
}
### 一维前缀和一维情况下,前缀和的核心思想是预先计算并存储数组的部分和以便后续查询。对于长度为 $n$ 的数组 `nums`,其对应的前缀和数组 `prefix_sum` 定义如下: $$ \text{prefix\_sum}[i] = \sum_{j=0}^{i} \text{nums}[j], $$ 其中 $ i \in [0, n-1] $。 以下是 Python 中的一维前缀和实现方式[^1]: ```python def build_prefix_sum(nums): prefix_sum = [0] * len(nums) prefix_sum[0] = nums[0] for i in range(1, len(nums)): prefix_sum[i] = prefix_sum[i - 1] + nums[i] return prefix_sum ``` 利用该前缀和数组可以快速得到任意子数组 `[l,r]` 的和: $$ \text{result} = \text{prefix\_sum}[r] - (\text{prefix\_sum}[l-1] \text{ if } l>0 \text{ else } 0). $$ --- ### 差分数组 差分数组主要用于处理频繁对某个区间内的元素进行增减操作的情况。给定一个初始数组 `nums`,可以通过构建差分数组来高效完成这些修改操作。定义差分数组 `diff` 如下: $$ \text{diff}[i] = \begin{cases} \text{nums}[i]-\text{nums}[i-1],& i > 0 \\ \text{nums}[i],& i = 0. \end{cases} $$ 下面是 JavaScript 中的差分数组构造与还原过程[^2]: ```javascript function buildDifferenceArray(nums) { const diff = new Array(nums.length); diff[0] = nums[0]; for (let i = 1; i < nums.length; ++i) { diff[i] = nums[i] - nums[i - 1]; } return diff; } // 还原原始数组 function restoreFromDiff(diff) { const result = []; result.push(diff[0]); for (let i = 1; i < diff.length; ++i) { result[i] = result[i - 1] + diff[i]; } return result; } ``` 当需要对某一段范围 `[L,R]` 增加值 `val` 时,在差分数组上仅需执行两步更新即可生效: $$ \text{diff}[L] += val,\quad \text{if R+1<length then }\text{diff}[R+1] -= val.$$ --- ### 二维前缀和 扩展到二维情况,假设有一个大小为 $m\times n$ 的矩阵 `matrix`,则二维前缀和数组 `prefix_matrix` 可以这样定义: $$ \text{prefix\_matrix}[i][j]=\sum_{p=0}^i\sum_{q=0}^j \text{matrix}[p][q]. $$ 下面是一个基于 Python 的二维前缀和实现方法: ```python def build_2d_prefix_sum(matrix): m, n = len(matrix), len(matrix[0]) prefix_matrix = [[0]*n for _ in range(m)] for i in range(m): for j in range(n): current_value = matrix[i][j] top = prefix_matrix[i-1][j] if i > 0 else 0 left = prefix_matrix[i][j-1] if j > 0 else 0 diagonal_top_left = prefix_matrix[i-1][j-1] if i > 0 and j > 0 else 0 prefix_matrix[i][j] = current_value + top + left - diagonal_top_left return prefix_matrix ``` 通过上述二维前缀和结构,可迅速获取任何矩形区域 `(x1,y1)` 到 `(x2,y2)` 内部所有数值总和: $$ S=\text{prefix\_matrix}[x2][y2]-(\text{prefix\_matrix}[x1-1][y2]\text{ if } x1>0 \text{ else } 0)-(\text{prefix\_matrix}[x2][y1-1]\text{ if } y1>0 \text{ else } 0)+(\text{prefix\_matrix}[x1-1][y1-1]\text{ if } x1>0 \text{ and } y1>0 \text{ else } 0). $$ --- ### 总结 以上分别介绍了如何在不同维度下应用前缀和以及差分技术解决实际问题中的复杂度优化需求。无论是针对单向序列还是多维数据表,合理运用这两种技巧都能显著提升程序性能效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橙子哥_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值