差分和前缀和

目录

什么是差分

什么是前缀和

用差分和前缀和解决问题

例题

题目描述

输入

输出

样例输入 

样例输出 

提示

题目解释

思路呈现

C++代码实现


什么是差分

例如假设一个数组    a [ n ]

构建一个差分数组    b [ 1 ] = a [ 1 ] - a [ 0 ]

                                 b [ 2 ] = a [ 2 ] - a [ 1 ]

                                 .....................

                                 b [ n ] = a [ n ] - a [ n - 1 ]

什么是前缀和

前缀和是指某序列的前n项和,可以把它理解为数学上的数列的前n项和,而差分可以看成前缀和的逆运算。合理的使用前缀和与差分,可以将某些复杂的问题简单化。

例如                a [ 5 ] = { 1 , 2 , 3 , 4 , 5 }

构建前缀和     s [ 1 ] = a [ 1 ]

                       s [ 2 ] = a [ 1 ] + a [ 2 ]

                       s [ n ] = a [ 1 ] +............ a [ n ]

用差分和前缀和解决问题

例题

题目描述

运动场修好后,体艺节马上要开始了。体艺节上要用N个计时器,每个计时器是一个中间显示秒数的方形电子表。如下图是显示5秒和201秒的两个计时器:

   

为了检查这些计时器的质量,晨晨创新地开发设计出一个新游戏,鼓动了M个同学报名参加。游戏开始时她把这N个计时器排成一行,开始时间都是0。然后她对这M个同学每个人发一条命令:
把第si个到第ti个计时器上时间都加1秒。
M个同学完成指令后,N个计时器上时间几乎都变了。晨晨要通过编计算机程序来计算所有计时器上的数字长度(位数)总和。例如下面N=3个计时器的数字长度分别是1、3、2,长度和是6(=1+3+2)。

   

输入

第1行:2个正整数N和M。(N,M均为大于0小于200000的整数)
下面有M行:每行2个整数s和t。(0<s<t<=N)

输出

 一整数,表示N个数总位数长度。

样例输入 
5  10
1  3
2  4
2  3
1  3
1  4
1  4
1  4
2  4
2  4
2  4
样例输出 
7
提示

最后计时器上的数字分别是:5、10、10、7、0
数字长度:1+2+2+1+1=7

题目解释

假设有五个计时器 0 0 0 0 0

假设一共执行2次

1---3个数加+1,2---4个数+1

第一次:1 1 1 0 0

第二次:1 2 2 1 0

数字长度:1 + 1 + 1 + 1 + 1 = 5(0也算1个)

思路呈现

建立原数组         a [ 5 ] = { 0 , 0 , 0 , 0 , 0 }

建立差分数组      b [ 5 ] = { 0 , 0 , 0 , 0 , 0 }

(这里下标都从1开始,便于说明计算)

首先第一步要让1--3的数+1

因为1--3的都+1了,所以差分数组中下标为2,3的数值是不会变的,原来为0-0,现在为1-1,都是0,只要差分数组下标为1的+1,1--3的数值都会跟着+1。

仅仅这样是不行的,因为这会让4--5的数也会跟着+1,解决这个问题的方法就是让下标为4的数值-1。

如果不加的话,原来是0-0=0,下标4和5都是0,改变了下标1的数值后,会变成0-0=0,下标4和5数值都是1,加上这个的话就是0-1=-1,这样下标4和5的数值就不会变化了

新的差分数组就变为     b_new [ 5 ] = { 1 , 0 , 0 , -1 , 0 }

新原数组(即前缀和)      因为 b [ n ] = a [ n ] - a [ n - 1 ]

                                     a_new [ n ] = b_new [ n ] + a_new [ n - 1 ]

                                     a_new [ 5 ] = { 1 , 1 , 1 , 0 , 0 }

C++代码实现

#include<iostream>
using namespace std;

#define N 200000

// 默认原数组都为0
int a[N] = { 0 };// 原数组
int b[N]; // 差分数组

int main()
{
	// n表示区间长度,m表示为执行次数
	int n, m;
	cin >> n >> m;
	int x, y;

	// 构建原差分数组
	for (int i = 1; i <= n; i++) // 下标从1开始计算
	{
		b[i] = a[i] - a[i - 1];
	}

	// 假设从x-y下标各+1
	for (int i = 1; i <= m; i++)
	{
		cin >> x >> y; // 输入改变区间

		// 新的差分数组
		b[x]++;
		b[y + 1]--;
	}

	// 求前缀和得要新原数组
	for (int i = 1; i <= n; i++)
	{
		// 新原数组
		a[i] = b[i] + a[i - 1]; 
		// b[i] = a[i] - a[i - 1] =>a[i] = b[i] + a[i - 1]
		// 差分数列的逆推
	}

	int c = 0;

	for (int i = 1; i <= n; i++)
	{
		c = c + a[i] / 10 + 1;
	}

	cout << c << endl;
	return 0;
}

  • 11
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Julie_Mol

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

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

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

打赏作者

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

抵扣说明:

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

余额充值