题目传送门
题目描述
如题,已知一个数列,你需要进行下面两种操作:
- 将某区间每一个数加上 k。
- 求出某区间每一个数的和。
输入格式
第一行包含两个整数 n,m,分别表示该数列数字的个数和操作的总个数。
第二行包含 n 个用空格分隔的整数,其中第 i 个数字表示数列第 i 项的初始值。
接下来 m 行每行包含 3 或 4 个整数,表示一个操作,具体如下:
1 x y k
:将区间[x,y] 内每个数加上 k。2 x y
:输出区间[x,y] 内每个数的和。
输出格式
输出包含若干行整数,即为所有操作 2 的结果。
输入输出样例
输入
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
输出
11
8
20
说明/提示
对于 30%的数据:n≤8,m≤10。
对于 70% 的数据:n≤10^3,m≤10^4。
对于 100% 的数据:1≤n,m≤10^5。
保证任意时刻数列中所有元素的绝对值之和 ≤1018。
【样例解释】
首先我声明一下:这题是线段树的模板题,因为树状数组是单点修改、区间求和的数据结构。在差分数组(也是单点修改、区间求和的数据结构)的加持下树状数组可以实现区间修改、单点求和的功能。
那么怎么才能实现从区间修改、单点求和转化为我们要的区间修改区间求和呢?这需要一点数学知识。
假设原数组是a,差分数组是d,那么
求a的前缀和:
然后我们以d出现的次数为基础重新排列,易得d[1]出现n次,d[2]出现n-1次……
则原式=
有一点要注意,因为数据是在不断更新着,并且树状数组来存d[i]和存d[i]*i并没有直接的倍数关系,所以我们要建两棵树tree1和tree2来分别d[i]和d[i]*i。
ps. python因自身特性而无法通过全部数据,经检测C++可以通过,时间复杂度没有问题。
# 前三个函数均为树状数组模板,但是修改和求值因为这题两树的特性而略有变动
def low_bit(x):
return x & -x
def add(x, k1, k2): # 单点修改
while x <= n:
tree1[x] += k1
tree2[x] += k2
x += low_bit(x)
def s(x): # 区间求值
x0 = x
ans1 = ans2 = 0
while x:
ans1 += tree1[x] # 分别累计两树的区间和
ans2 += tree2[x]
x -= low_bit(x)
return (x0+1)*ans1-ans2
n, m = map(int, input().split())
a = [0]+list(map(int, input().split()))
tree1 = [0]*(n+1)
tree2 = [0]*(n+1)
for i in range(1, n+1):
add(i, a[i]-a[i-1], i*(a[i]-a[i-1])) # 建树的过程和更新并没有区别,注意两树更新的值是不一样的
for _ in range(m):
a = list(map(int, input().split()))
x, y = a[1], a[2]
if a[0] == 1:
k1 = a[3]
add(x, k1, x*k1)
if y < n: # 如果y已经到最后一位了,就只需要更新d[x]
add(y+1, -k1, -(y+1)*k1)
else:
print(s(y)-s(x-1)) # 树状数组通过相减实现区间和的询问
看我写得这么辛苦点个赞吧Orz