前缀和/差分

一维前缀和
问题引入

数组arr= {1,2,3,4,5},问[2,4]的和

解决思想

定义前缀和数组sum={1,3,6,10,15};例如sum[1]=3,sum[4]=15;

[l,r]的和就等于 sum[r]-sum[l-1](当l>0时)

sum[r] (当l=0时)

如[2,4]的和等于sum[4]-sum[1]=12。

代码
#include<bits/stdc++.h>
using namespace std;
#define add(l,r) (l?sum[r]-sum[l-1]:sum[r]) //当l!=0时返回前者 
int main()
{
	int arr[5]={1,2,3,4,5};
	int sum[5];
	sum[0]=arr[0];
	for(int i=1;i<5;i++)
		sum[i]=sum[i-1]+arr[i];
	cout<<add(2,4);
	return 0;
}
一维差分
问题引入

数组arr={1,2,3,4,5},现在对 [0,2]-1,再对 [2,4]+3,问最终的数组

解决思想

arr 1 2 3 4 5

d 1 1 1 1 1 (arr后一位减去前一位)

sumd 1 2 3 4 5 (d的前缀和,会发现就等于arr)

那么对于[l,r]+v

首先 [l,r]+v 进行 d[l]+v,d[r+1]-v

然后 将得到的差分数组进行前缀和便得到答案

如题,得到差分数组为 0 1 4 2 1

答案为前缀和 0 1 5 7 8

代码
#include<bits/stdc++.h>
using namespace std;
int d[6]={0};//防止越界 
void add(int l,int r,int v)
{
	d[l]+=v;
	d[r+1]-=v;
} 
int main()
{
	int arr[5]={1,2,3,4,5};
	add(0,2,-1);
	add(2,4,3);
	for(int i=1;i<5;i++)
		d[i]+=d[i-1];
	for(int i=0;i<5;i++)
	{
		arr[i]+=d[i];
		cout<<arr[i]<<" ";
	}
	memset(d,0,sizeof(0));//对d数组进行清零,便于下一次操作 
	return 0;
}
二维前缀和
问题引入

给定一个数组 {1,2,3,4} ,问(1,1)到(2,2)的和。 {5,6,7,8} {1,2,3,4}

解决思想

先解决(0,0)到(2,2)的和再减去多出来的部分

代码
#include<bits/stdc++.h>
using namespace std;
int arr[3][4]={{1,2,3,4},
			   {5,6,7,8},
			   {1,2,3,4}};
int sum[3][4];
void pre_sum(){
	sum[0][0]=arr[0][0];
	for(int j=1;j<4;j++)
		sum[0][j]=sum[0][j-1]+arr[0][j];
	for(int i=1;i<3;i++)
		sum[i][0]=sum[i-1][0]+arr[i][0];
	for(int i=1;i<3;i++)
	{
		for(int j=1;j<4;j++)
		{
			sum[i][j]=arr[i][j]+sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1];
		}
	}
	
}
int get_sum(int x1,int y1,int x2,int y2)
{
	if(!x1&&!y1) return sum[x2][y2];
	if(!x1)return sum[x2][y2]-sum[x2][y1-1];
	if(!y1)return sum[x2][y2]-sum[x1-1][y2];
	return sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1];
}
int main()
{
	pre_sum();
	cout<<get_sum(1,1,2,2);
	return 0;
}
二维差分
问题引入

已知数组 arr3={{1,2,3,4}, ,求(1,1)到(2,2)的部分加了1后的数组。 {1,2,3,4}, {1,2,3,4}}

解决思想

结合一维差分和前缀和。确定一个差分标记数组{0},对其中的一些关键点进行加减,再运用二维前缀和,这样就能得到在指定部分进行了加减的标记数组,在整体加上原数组即可。

代码
#include<bits/stdc++.h>
using namespace std;
int arr[3][4]={{1,2,3,4},
			   {1,2,3,4},
			   {1,2,3,4}};
int sum[3][4]={0},d[3][4]={0};
void add(int x1,int y1,int x2,int y2,int v)
{
	d[x1][y1]+=v;
	d[x1][y2+1]-=v;
	d[x2+1][y1]-=v;
	d[x2+1][y2+1]+=v;
}
void get_sum(){
	sum[0][0]=d[0][0];
	for(int j=1;j<4;j++)
		sum[0][j]=sum[0][j-1]+d[0][j];
	for(int i=1;i<3;i++)
		sum[i][0]=sum[i-1][0]+d[i][0];
	for(int i=1;i<3;i++)
	{
		for(int j=1;j<4;j++)
		{
			sum[i][j]=d[i][j]+sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1];
		}
	}
	for(int i=0;i<3;i++){
		for(int j=0;j<4;j++){
			arr[i][j]+=sum[i][j];
		}
	}
}
void print()
{
	for(int i=0;i<3;i++)
	{
		for(int j=0;j<4;j++)
		{
			cout<<arr[i][j]<<" ";
		}
		cout<<endl;
	}
}
int main()
{
	add(1,1,2,2,1);
	get_sum();
	print();
	return 0;
}

>W<

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值