问题描述
给定数组a,给定操作q个操作: l , r , c l,r,c l,r,c,代表将a数组从 a [ l ] a[l] a[l]到 a [ r ] a[r] a[r]全部加上c,问q个操作后a数组的结果
Input
第一行输入n和q
(
1
≤
n
,
q
≤
2
×
1
0
5
)
(1\le n,q\le 2\times 10^5)
(1≤n,q≤2×105),代表数组有n个元素,q个操作
第二行输入数组a,其中
−
1
0
6
≤
a
i
≤
1
0
6
-10^6\le a_i\le 10^6
−106≤ai≤106。
后面q行,每行输入三个数字
l
,
r
,
c
(
1
≤
l
,
r
≤
n
,
−
1
0
5
≤
c
≤
1
0
5
)
l,r,c(1\le l,r\le n,-10^5\le c\le 10^5)
l,r,c(1≤l,r≤n,−105≤c≤105)。
Output
输出a数组,中间用空格隔开
Sample input
4 2
-3 6 8 4
4 4 -2
3 3 1
Sample output
-3 6 9 2
解题思路
分析
首先肯定不能用暴力来做,不超时才有鬼。
对于处理一个数组,将其区间+或-某个数的时候,有两种方法:线段树或者差分。
差分更新的时间复杂度是
O
(
1
)
O(1)
O(1),查询的时间复杂度是
O
(
n
)
O(n)
O(n)。
线段树更新的时间复杂度是
O
(
l
o
g
n
)
O(logn)
O(logn),查询的时间复杂度也是
O
(
l
o
g
n
)
O(logn)
O(logn)。
因此这道多次更新,最后一次查询的题,应该是使用差分来做。
差分介绍
差分数组就是存储相邻两个数的差,对于数组 a [ 1 ⋯ n ] a[1\cdots n] a[1⋯n],其差分数组b的计算公式是:
b [ 1 ] = a [ 1 ] , b [ i ] = a [ i ] − a [ i − 1 ] ( i > 1 ) b[1]=a[1],b[i]=a[i]-a[i-1](i>1) b[1]=a[1],b[i]=a[i]−a[i−1](i>1)
将其转化为原数组的方法就是求前缀和,即 ∑ i = 1 j b [ j ] = a [ j ] \sum_{i=1}^{j}{b[j]}=a[j] ∑i=1jb[j]=a[j]。
可见,差分数组存储的是原数组的差。那这东西有什么用呢?
我们先假设存在a数组与差分数组b如下:
数组 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
a | 5 | 2 | 3 | 1 | 4 |
b | 5 | -3 | 1 | -2 | 3 |
我们对于a数组区间 [ l , r ] [l,r] [l,r]上的操作,可以转化为对其差分数组b中 b [ l ] b[l] b[l]和 b [ r + 1 ] b[r+1] b[r+1]的操作。如,对区间 [ 2 , 4 ] [2,4] [2,4]上的所有元素+3,可以转化为 b [ 2 ] + 3 , b [ 5 ] − 3 b[2]+3,b[5]-3 b[2]+3,b[5]−3,a数组和b数组更改后如下所示:
数组 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
a | 5 | 5 | 6 | 4 | 4 |
b | 5 | 0 | 1 | -2 | 6 |
此时的b数组仍然是更改后a数组的差分,所以, a [ l ⋯ r ] + c a[l\cdots r]+c a[l⋯r]+c 等价于 b [ l ] + c , b [ r + 1 ] − c b[l]+c,b[r+1]-c b[l]+c,b[r+1]−c。
这样,每次对区间的操作,都可以转化为对b数组两个点的操作,大大提升了更新效率。最后将b求前缀和就可以得到操作完成的a数组。
差分的原理
那为什么可以样操作呢?关键在于最后b数组转化为a数组的过程是一个求前缀和的过程。
如果我们将b数组中 b [ l ] + c b[l]+c b[l]+c,那么从 l l l开始向后的所有元素,在求前缀和的时候,都将这个c加进去了,所以结果就是区间 [ l , n ] [l,n] [l,n]都加上了c。
我们将 b [ r + 1 ] − c b[r+1]-c b[r+1]−c,那么从 r + 1 r+1 r+1开始,向后的所有元素在求前缀和的时候,都经历了一个 + c +c +c后又 − c -c −c的过程,也就是没有变化。
经过上面两个操作,我们相当于将 a [ l ] a[l] a[l]到 a [ r ] a[r] a[r]之间到所有元素都加上了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;
}