非常高效的数据结构,可以满足大部分需求。可以区间求和、区间最大值、单点修改。一般不能区间修改(就这个结构而言)
图片出处:树状数组(求逆序对)_baby的我的博客-程序员宅基地_树状数组求逆序对,已征得作者同意使用
P3374 【模板】树状数组 1 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
def lowbit(x): # 求的一个数的二进制的第一个1的位置,比如6,它的二进制:0110,从右往左第一个1的位置是1(从零开始),那么lowbit(6)就等于2的1次方就等于2
return x & -x
def query(x, y): # 查询[x+1, y]
ans = 0
while y > x:
ans += tr[y]
y -= lowbit(y)
while x > y:
ans -= tr[x]
x -= lowbit(x)
return ans
def add(x, k):
while x <= n:
tr[x] += k
x += lowbit(x)
n, m = map(int, input().split())
tr = [0]*(n+1)
a = list(map(int, input().split()))
for i in range(1, n+1):
tr[i] += a[i-1]
if (fa := i+lowbit(i)) <= n:
tr[fa] += tr[i]
for _ in range(m):
i, j, k = map(int, input().split())
if i == 1:
add(j, k) # 向上叠加
else:
print(query(j-1, k))
for i in range(1, n):
tr[i] = d[i] - d[i-lowbit(i)] # d是前缀和数组, 因为lowbit(i)就是tr(i)维护的原数组的长度
原本nlogn就是因为有重复计算的数组,那么只要把已经建好的部分直接累加到后面就可以保证每个都只计算一次,即O(n)
for i in range(1, n):
tr[i] = a[i]
j = 1
flag = lowbit(i) # 对应上面的图,每个数字下面连的就是要加上去的
while j < flag:
tr[i] += tr[i-j]
j <<= 1
for i in range(1, n):
tr[i] += a[i]
if (fa := i+lowbit(i)) <= n:
tr[fa] += tr[i] # +最低位变父亲
code2\3先进的地方就在于不是利用a来推,而是利用已经建好的树,避免了a的重复叠加。code2和code3进行操作的次数一定是一样的,code2看起来多但是有些节点甚至是没子结点的
def add(x, k):
while x <= n:
tr[x] += k
x += lowbit(x)
def query(x):
ans = 0
while x:
ans += tr[x]
x -= lowbit(x)
return ans
print(query(j)-query(i-1))
def query(x, y):
"""对区间和而言是[x+1, y], 因为到x都被减掉了"""
ans = 0
while y > x:
ans += tr[y]
y -= lowbit(y)
while x > y:
ans -= tr[x]
x -= lowbit(y)
P3368 【模板】树状数组 2 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
def lowbit(x): # 树状数组可借助差分数组实现区间修改,单点求值
return x & -x
def add(x, k, t): # 树状数组的单点修改,原数组的区间修改
while x <= n:
t[x] += k
x += lowbit(x)
def query(x, t):
ans = 0
while x:
ans += t[x]
x -= lowbit(x)
return ans
n, m = map(int, input().split())
a = [0]+list(map(int, input().split())) # 和前缀和一样前面要留一个0
tr = [0]*(n+1)
for i in range(1, n+1):
x = a[i]-a[i-1] # 利用差分数组实现区间修改,单点查询
tr[i] += x
if (fa := i+lowbit(i)) <= n:
tr[fa] += tr[i]
for _ in range(m):
a = list(map(int, input().split()))
if len(a) == 4:
x, y, k = a[1], a[2], a[3]
add(x, k, tr)
if y < n:
add(y+1, -k, tr) # 一定要注意正负
else:
print(query(a[1], tr))
P3372 【模板】线段树 1 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
首先我声明一下:这题是线段树的模板题,因为树状数组是单点修改、区间求和的数据结构。在差分数组(也是单点修改、区间求和的数据结构)的加持下树状数组可以实现区间修改、单点求和的功能。
那么怎么才能实现从区间修改、单点求和转化为我们要的区间修改区间求和呢?这需要一点数学知识。
假设原数组是a,差分数组是d,那么
求a的前缀和: ∑ i = 1 n a [ i ] = ∑ i = 1 n ∑ j = 1 i d [ j ] \large \sum ^n_{i=1}a[i] = \sum^n_{i=1}\sum^i_{j=1}d[j] ∑i=1na[i]=∑i=1n∑j=1id[j]
然后我们以d出现的次数为基础重新排列,易得d[1]出现n次,d[2]出现n-1次……
则原式= ∑ i = 1 n d [ i ] ∗ ( n − i + 1 ) = ( n + 1 ) ∑ i + 1 n d [ i ] − ∑ i = 1 n d [ i ] ∗ i \large \sum ^n_{i=1}d[i]*(n-i+1)=(n+1)\sum^n_{i+1}d[i]-\sum^n_{i=1}d[i]*i ∑i=1nd[i]∗(n−i+1)=(n+1)∑i+1nd[i]−∑i=1nd[i]∗i
有一点要注意,因为数据是在不断更新着,并且树状数组来存d[i]和存d[i]*i并没有直接的倍数关系,所以我们要建两棵树tree1和tree2来分别维护d[i]和d[i]*i。
ps. python因自身特性而无法通过全部数据,经检测C++可以通过,时间复杂度没有问题。
# 前三个函数均为树状数组模板,但是修改和求值因为这题两树的特性而略有变动
def lowbit(x):
return x & -x
def add(x, k1, k2, t1, t2): # 单点修改
while x <= n:
t1[x] += k1
t2[x] += k2
x += lowbit(x)
def query(x, t1, t2): # 区间求值
x0 = x
ans1 = ans2 = 0
while x:
ans1 += t1[x] # 分别累计两树的区间和
ans2 += t2[x]
x -= lowbit(x)
return (x0+1)*ans1-ans2
n, m = map(int, input().split())
a = [0]+list(map(int, input().split()))
tr1 = [0]*(n+1)
tr2 = [0]*(n+1)
for i in range(1, n+1):
x = a[i]-a[i-1]
tr1[i] += x
tr2[i] += i*x
if (fa := i+lowbit(i)) <= n:
tr1[fa] += tr1[i]
tr2[fa] += tr2[i]
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, tr1, tr2)
if y < n: # 如果y已经到最后一位了,就只需要更新d[x]
add(y+1, -k1, -(y+1)*k1, tr1, tr2)
else:
print(query(y, tr1, tr2)-query(x-1, tr1, tr2)) # 树状数组通过相减实现区间和的询问
[P2880 USACO07JAN] Balanced Lineup G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
def lowbit(x):
return x & -x
def query1(x, y, t1, a):
if y > x:
s = y-lowbit(y)
return max(query1(x, s, t1, a), t1[y]) if s >= x else max(query1(x, y-1, t1, a), a[y])
return a[x]
def query2(x, y, t2, a):
if y > x:
s = y-lowbit(y)
return min(query2(x, s, t2, a), t2[y]) if s >= x else min(query2(x, y-1, t2, a), a[y])
return a[x]
n, q = map(int, input().split())
a = [0]+[int(input()) for _ in range(n)]
tr1 = [0]*(n+1) # max
tr2 = [float('inf')]*(n+1) # min
for i in range(1, n+1): # 建树
x = a[i]
if x > tr1[i]:
tr1[i] = x
if x < tr2[i]:
tr2[i] = x
if (fa := i+lowbit(i)) <= n:
if tr1[i] > tr1[fa]:
tr1[fa] = tr1[i]
if tr2[i] < tr2[fa]:
tr2[fa] = tr2[i]
for _ in range(q):
x, y = map(int, input().split())
print(query1(x, y, tr1, a)-query2(x, y, tr2, a))
说明:因为python自身特性,部分点会TLE
# 初始化
tr1 = [0]*(n+1) # max
tr2 = [float('inf')]*(n+1) # min
# 开始建树
for i in range(1, n+1):
x = a[i]
if x > tr1[i]: # 最大值的更新
tr1[i] = x
if x < tr2[i]: # 最小值的更新
tr2[i] = x
if (fa := i+lowbit(i)) <= n:
if tr1[i] > tr1[fa]:
tr1[fa] = tr1[i]
if tr2[i] < tr2[fa]:
tr2[fa] = tr2[i]
直接传递给相邻的父节点,实现O(n)建树
def query1(x, y, t1, a):
"""max查询"""
if y > x:
s = y-lowbit(y)
return max(query1(x, s, t1, a), t1[y]) if s >= x else max(query1(x, y-1, t1, a), a[y])
return a[x]
def query2(x, y, t2, a):
"""min查询"""
if y > x:
s = y-lowbit(y)
return min(query2(x, s, t2, a), t2[y]) if s >= x else min(query2(x, y-1, t2, a), a[y])
return a[x]
据上面树状数组的特征,便可知在y-lowbit(y) >= x时可以拆成tr[y]维护的部分和tr[y]之外的部分(在相等时外面的部分就只有一个),即t1[y] ,query1(x, s, t1, a)。而在y-lowbit(y) < x时将a[y]挑出剩下的部分继续处理。一直到x = y时,就只有一个a[x],即可将其取出。
P1908 逆序对 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
def low_bit(m):
return m & -m
n = int(input())
a = list(map(int, input().split()))
c = dict(zip(sorted(a), range(1, n+1))) # 为实现数据的离散化,强行减少数据使用空间,所以等价地将数字替换成跟他们大小顺序对应的数字
tree = [0]*(n+1)
ans = 0
for i in range(len(a)-1, -1, -1): # 让a逆序遍历,因为要的是顺序的效果,所以如果在加入的时候已经存在比加入的还小的,那就是要求的逆序对
num2 = c[a[i]]
x = num2-1
while x: # 区间求和(到零才退出)
ans += tree[x]
x -= low_bit(x)
while num2 <= n: # 单点更新
tree[num2] += 1
num2 += low_bit(num2)
print(ans)
文章浏览阅读3.5w次,点赞64次,收藏87次。在CentOS8上执行下面命令时报错yum install epel-releaseCentOS Linux 8 - AppStream 23 B/s | 38 B 00:01 Error: Failed to download metadata for repo 'appstream': Cannot prepare internal mirrorlist: No URLs in mirrorlist原因在2022年1..._centos linux 8 - appstream 63 b/s | 38 b 00:00 error: failed to download met
文章浏览阅读774次。学习于逆向工程核心原理IA-32指令章节格式x86指令格式指令前缀出现特定操作码时用作补充说明,图中的冒号前的64就是指令前缀操作码实际的指令,如图中的FF、89、80都是操作码ModR/M辅助说明操作码的操作数(操作数的个数、种类[寄存器、内存地址、常量]),图中的SIB..._ebxmodr
文章浏览阅读735次。选择科目测一测我能上哪些大学选择科目领取你的专属报告>选择省份关闭请选择科目确定v>计算机类专业一直位于热门专业行列,主要是因为计算机行业的工资待遇是很多行业都不能相比的,所以吸引了众多学子踊跃报考该专业。那么计算机专业最强的大学有哪些?哪个大学的计算机专业全国第一?本期小编将为大家解答相关问题。一、计算机专业最强的大学有哪些?根据教育部全国第四轮学科评估结果进行参考,计算机专业最强的..._北京计算机最好学校
文章浏览阅读1.3k次,点赞2次,收藏3次。vue3 excel导出_vue3导出excel表格
文章浏览阅读1.2w次,点赞4次,收藏11次。// Copyright (c) 2014软件技术2班_用c#编写程序实现输入摄氏温度c的值输出对应华氏温度f的值
文章浏览阅读2.4k次。LOG SocketManager: Tried connecting socket when engine isn't open. ConnectingLOG SocketManager: Adding engineLOG SocketIOClient{/}: Handling event: statusChange with data: [connecting, 2]LOG SocketMan..._苹果文件共享socket未连接
文章浏览阅读1.9k次,点赞3次,收藏7次。Vue.js是一套构建用户界面的渐进式框架。自诞生之日起就受到了广泛的关注,和广大的前端开发者一样,笔者着迷于其简洁友好的语法、强大的单文件组件以及丰富的生态系统。通过这样一个简单但全面的开源项目分析,笔者希望可以入门这个框架且做一定的深入了解。_vue2-happyfri
文章浏览阅读488次。最常用的编程元素一: 常量与变量 1, 常量:就是固定不变的量,一旦被定义,他的值就不会被改变 。 往往用final修饰。 2,变量:变量是利用声明的方式,将内存中的某个块保留下来。 (1)说明 1:要求在使用一个变量之前要对变量的类型加以声明。 2:java中一个变量的声明就是一条完整的语句
文章浏览阅读589次。三点照明法_[maya学习笔记(1)] 视窗的基本操作
文章浏览阅读1.2w次,点赞2次,收藏7次。之前在做一个音频特征提取的批量处理程序,老是出现内存泄露问题,用Visual Leak Detector(VLD)工具做了下检测,检测出了一些问题,解决后还是会有问题。之后继续排查,因为我的代码中,大量的音频相关处理的数据都存成了vector变量,推测是不是vector变量的析构问题,上网查了些资料,现写出解决过程:1、关于Visual Leak Detector的配置与使用主要也_c++ vector是否会造成内存泄漏
文章浏览阅读77次。现在流行的 是Spring Boot 做的 SSM框架:SpringMVC + Spring + MyBatisVUE + Spring Boot主要还是结合自己的实际水平来。如果你真的在选题这一方面完全没思路的话,下面有一些题目可以供你参考下,是之前上半年完成的部分的毕设程序,具体获取见文末。面对老师五花八门的设计要求,首先自己要明确好自己的题目方向,并且与老师多多沟通,用什么编程语言,使用到什么数据库,确定好了,在开始着手毕业设计。ssm基于ssm的校园失物招领平台h5xpq。
文章浏览阅读314次。1、首先需要安装python和flask,这个是必须的嘛。2、我们这里实现的是一个POST功能的简单接口。from flask import Flask, request, jsonifyimport jsonapp = Flask(__name__)app.debug = [email protected]('/add/student/',methods=['post'])def add_stu():..._python flask post 接口 app.route