问题描述
给定数组a,给定操作q个操作: ,代表将a数组从 到 全部加上c,问q个操作后a数组的结果
Input
第一行输入n和q
,代表数组有n个元素,q个操作
第二行输入数组a,其中
。
后面q行,每行输入三个数字
。
Output
输出a数组,中间用空格隔开
Sample input
4 2
-3 6 8 4
4 4 -2
3 3 1
Sample output
-3 6 9 2
解题思路
分析
首先肯定不能用暴力来做,不超时才有鬼。
对于处理一个数组,将其区间+或-某个数的时候,有两种方法:线段树或者差分。
差分更新的时间复杂度是
,查询的时间复杂度是
。
线段树更新的时间复杂度是
,查询的时间复杂度也是
。
因此这道多次更新,最后一次查询的题,应该是使用差分来做。
差分介绍
差分数组就是存储相邻两个数的差,对于数组 ,其差分数组b的计算公式是:
将其转化为原数组的方法就是求前缀和,即 。
可见,差分数组存储的是原数组的差。那这东西有什么用呢?
我们先假设存在a数组与差分数组b如下:
数组 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
a | 5 | 2 | 3 | 1 | 4 |
b | 5 | -3 | 1 | -2 | 3 |
我们对于a数组区间 上的操作,可以转化为对其差分数组b中 和 的操作。如,对区间 上的所有元素+3,可以转化为 ,a数组和b数组更改后如下所示:
数组 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
a | 5 | 5 | 6 | 4 | 4 |
b | 5 | 0 | 1 | -2 | 6 |
此时的b数组仍然是更改后a数组的差分,所以, 等价于 。
这样,每次对区间的操作,都可以转化为对b数组两个点的操作,大大提升了更新效率。最后将b求前缀和就可以得到操作完成的a数组。
差分的原理
那为什么可以样操作呢?关键在于最后b数组转化为a数组的过程是一个求前缀和的过程。
如果我们将b数组中 ,那么从 开始向后的所有元素,在求前缀和的时候,都将这个c加进去了,所以结果就是区间 都加上了c。
我们将 ,那么从 开始,向后的所有元素在求前缀和的时候,都经历了一个 后又 的过程,也就是没有变化。
经过上面两个操作,我们相当于将 到 之间到所有元素都加上了c。
完整代码
//#pragma GCC optimize(2)//比赛禁止使用!
//#pragma G++ optimize(2)
//#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
const int maxn=200000+10;
int n,q,a[maxn],b[maxn];
//a是原始数据,b是差分数组
int getint()
{
int x=0,s=1;
char ch=' ';
while(ch<'0' || ch>'9')
{
ch=getchar();
if(ch=='-') s=-1;
}
while(ch>='0' && ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*s;
}
int main()
{
n=getint(); q=getint();
for (int i=1; i<=n; i++)
a[i]=getint();
for (int i=1; i<=n; i++)//求差分
b[i]=a[i]-a[i-1];
for (int i=1; i<=q; i++)
{
int l=getint(),r=getint(),c=getint();
b[l]+=c; b[r+1]-=c;
}
long long ans=0;
for (int i=1; i<=n; i++)//前缀和将差分转换回去
{
ans+=b[i];
printf("%lld ",ans);
}
putchar('\n');
return 0;
}