介绍
给定一个序列 \(a\) ,其相邻两项之差构成另一个序列 \(s\),则称 \(s\) 为差分数组。
下图是一个简单的例子:
差分数组主要适用于区间修改。如上表中的例子,我们进行该操作:将下标区间为\([1,4]\)内的元素都加上3。暴力方法是进行遍历,给每一个元素都加上该数值,但是这样的话时间消耗很大,但对于差分数组而言,只有在其下标区间的端点处才会有数值大小的改变,而其余地方是不变的,在上述区间内操作后结果如下:
事实上,当对一个区间进行增减某个值的时候,其差分数组只有对应区间的左右端点值会变化,而且左右端点的值变化相反。如对于区间\([a,b]\)中加上一个数 \(x\), 则有\(s[a] += x, s[b + 1] -=x\).
由于上述性质的存在,显然差分数组的作用就是求对一个区间多次修改之后的数组。我们有:$$a[i]=a[i-1]+s[i]$$
除此之外,序列 \(a\) 中的某元素 \(a[i]\) 可以用差分数组 \(s\) 中的元素表示:\(a[i]=a[i]-a[i-1]+a[i-1]-a[i-2]+...+a[1]-a[0]\) , 显然后面那一串的和为差分数组 \(s\) 的前 \(i\) 项和 \(\sum_{k=1}^is[k]\). 此外,如果我们只想快速得到少量位置的现值的话,应该用树状数组维护差分而不是前缀和。
例题
Color the ball
代码如下:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 100002;
int ball[N];
int diff[N];
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n;
int x, y;
while(cin >> n && n)
{
memset(ball, 0, sizeof(ball));
memset(diff, 0, sizeof(diff));
for(int i = 0; i < n; i++)
{
cin >> x >> y;
diff[x]++;
diff[y + 1]--;
}
for(int i = 1; i <= n; i++)
{
ball[i] = ball[i - 1] + diff[i];
}
for(int i = 1; i < n; i++)
{
cout << ball[i] << " ";
}
cout << ball[n] << endl;
}
return 0;
}