本质上当然可以用线段树,但还是可以用树状数组维护一个差分。
P3368 【模板】树状数组 2
题目描述
如题,已知一个数列,你需要进行下面两种操作:
1.将某区间每一个数数加上$x$。
2.求出某一个数的值。
输入输出格式
输入格式
第一行包含两个整数$N,M$,分别表示该数列数字的个数和操作的总个数。
第二行包含$N$个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。
接下来$M$行每行包含$2$或$4$个整数,表示一个操作,具体如下:
操作1: 格式:1 x y k 含义:将区间$[x,y]$内每个数加上$k$。
操作2: 格式:2 x 含义:输出第x个数的值。
输出格式
输出包含若干行整数,即为所有操作2的结果。
INPUT & OUTPUT’s examples
Input’s eg #1
1 | 5 5 |
Output’s eg #1
1 | 6 |
说明
时空限制:1000ms,128M
数据规模:
对于30%的数据:$N \leq , M \leq 10$
对于70%的数据:$N \leq 10000,M \leq 10000$
对于100%的数据:$N \leq 500000,M \leq 500000$
样例说明
分析
看起来这道题似乎和树状数组的作用正好相反,那怎样用树状数组求解呢?
虽然可以用线段树一遍过,但是线段树码量辣么大,懒得写啊。
这时候,差分作为一个$O(n)$的优秀算法就体现出它的作用了。
在树状数组中我们可以用前$k$项的和来表示第$k$个数。
对于单点求值,直接进行printf("%d",Query(x))
即可。
当我们需要对区间$[l,r]$进行修改的时候,仅需要在差分数组(这里我们用树状数组维护)l
和r+1
的位置上分别+v
和-v
即可。
我们用树状数组来维护差分的过程,对于每一次区间修改的操作,可以:1
2
3scanf("%d%d%lld",&l,&r,&v);
Update(l,v);
Update(r+1,-v);
代码
1 | /* Headers */ |