目录
什么是差分
例如假设一个数组 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;
}