1、lowbit
lowbit (x):可以求出数字x的最低位、 性质:lowbit(x) = x & (-x)。
上述的性质是由于补码的特性导致的,正数x的补码等于二进制,负数的补码等于反码 x + 1。
看一个例子:
2、树状数组
树状数组:利用lowbit高效存前缀和。原始数组a:对应的树状数组tree如上图。tree[i] = sum(a[i - lowbit(i) + 1],....,a[i]).
只要按照上述规律存储,可以利用lowbit实现:①:快速求前缀和。②、更新数组a。
模版题
理论方面的我号蒙,代码的实现过程我也特别蒙,不过代码模版我还是能够熟练的敲出来。
例题的题目及链接
题目描述
一个N个方格开始每个格子里都有一个整数。
你需要动态进行一些操作,操作有以下两种类型:
①、提问:要求某一个特定的字区间[a, b]中所以元素的元素和;
②、修改:指定某一个格子x,令x加上或者减去一个特定的值A。
现在要求你能对每一提问做出正确的回答。
输入描述
输入文件第一行为一个整数N,接下来一行包含n个整数,表示格子中原来的整数。接下来一个正整数m,再接下来有m行,表示m个询问。每个询问的第一个整数表示询问代号,询问代号表示增加,后面的两个数x和A\表示X 上的位置增加A,询问代号表示区间求和, 后面两个整数表示a和b,表示要求[a, b]之间的区间和。
1 <= N, X, A <= 100000, m <= 10000。
输出描述
共m行,每行一个整数, 表示每个提问的答案。
def lowbit(x):
return x & (-x)
# 1、树状数组前缀和
# 求前缀和a[i] + ... + a[x]
# 从x出发,往左走,每次减去lowbit(x)
def query(x, tree):
"""
:param x:前x项的前缀和
:param tree:树状数组
:return:ans 前x项的前缀和
"""
ans = 0
while x:
ans += tree[x]
x -= lowbit(x)
return ans
# 实现单点的更新
# 如果将a[x] + y?
# 从x出发向右走,每次加上lowbit(x),所以经过的点都加上y
def add(x, y, tree, n):
while x <= n:
tree[x] += y
x += lowbit(x)
n = int(input())
a = [0] + list(map(int, input().split()))
tree = [0] * (n + 1)
# 初始化的过程构造树状数组
for i in range(1, n + 1):
add(i, a[i], tree, n)
m = int(input())
for _ in range(m):
op, a, b = map(int, input().split())
if op == 1:
add(a, b, tree, n)
else:
print(query(b, tree) - query(a - 1, tree))
基于模版的题目
把题目中的公式经过变形得到下面的公式:
def lowbit(x):
return x & -x
def query(x):
ans = 0
while x:
ans += tree[x]
x -= lowbit(x)
return ans
def add(x, y):
while x <= n:
tree[x] += y
x += lowbit(x)
n, m = map(int, input().split())
a = [0] + list(map(int, input().split()))
tree = [0] * (n + 1)
for i in range(1, n + 1):
add(i, a[i])
for i in range(m):
op = list(map(int, input().split()))
if op[0] == 1:
x, y = op[1], op[2]
add(x, y - a[x])
a[x] = y # 不要忘记这一步
else:
# 题目中描述查询第x的愉悦值
x = op[1]
print((2 * x - n - 1) * a[x] + query(n) - query(x - 1) - query(x))