题目:
来源:
P2046 - [蓝桥杯2022初赛] 重新排序 - New Online Judge (ecustacm.cn)
分析:
当然可以使用暴力解法,主要思路如下:
1.统计每个位置出现的次数。
2.按照出现次数排序,同时将数组排序。
3.按照位置求和,然后减去原结果,算出增量。
暴力解法可以见下面博客:
(70条消息) 2022蓝桥杯A组Python_蓝桥杯矩形拼接_Mooan的博客-CSDN博客
不过可以注意到以下特点:
1.需要求解原答案,这是区间求和。
2.需要更改区间范围的出现次数。
前缀和便于查询不利于更改,而普通数组便于更改的可能不利于查询。
因此,找到了线段树算法,该算法不仅利于查询区间求和,而且还可以利用lazy标记进行区间更改。
下面开始讲解线段树,具体的可以参考博客:
(70条消息) 线段树Segment Tree(python代码实现)_python 线段树_卷不动的程序猿的博客-CSDN博客
首先是线段树的建立,线段树是二叉树,而且还是完全二叉树,因此可以采用建堆的思想来进行线段树的建立。主要思想为利用数组下标和递归,代码如下:
def build_tree(arr,tree,node,start,end):
if start==end:
tree[node] = arr[start]
else:
mid = (start+end)//2
l_node = 2*node+1
r_node = 2*node+2
build_tree(arr,tree,l_node,start,mid)
build_tree(arr,tree,r_node,mid+1,end)
tree[node] = tree[l_node] + tree[r_node]
其中tree为线段树数组,arr为录入数组,node为节点下标,start,end用于递归。
该题目无需对线段树即tree数组进行更改。
下面给出查询函数:
def query_tree(arr,tree,count,node,start,end,L,R):
if R<start or L>end:
return 0
elif start == end:
count[node] += 1
return tree[node]
elif L<=start and end<=R:
count[node] += 1
return tree[node]
else:
mid = (start+end)//2
l_node = 2*node+1
r_node = 2*node+2
sum_left = query_tree(arr,tree,count,l_node,start,mid,L,R)
sum_right = query_tree(arr,tree,count,r_node,mid+1,end,L,R)
return sum_left+sum_right
与原博客中,我添加了一个count数组,这是为了统计各个位置查询次数。
当全部查询完后,我们需要将各个节点的count向下拉,拉到叶节点,即pushdown函数
def pushdown(count,size):
num = size//2
for i in range(num):
if count[i]!=0:
l_node = 2*i+1
r_node = 2*i+2
count[l_node] += count[i]
count[r_node] += count[i]
count[i] = 0
size是线段树节点的数量,这个很好计算,比如arr长度为n,则建立先线段树的节点数为2*n-1。
主函数如下:
if __name__=="__main__":
n = eval(input())
arr = [ int(i) for i in input().split(' ')] # 数据
#print(arr)
max_len = 4*n # 设置线段树的大小
tree = [0] * max_len # 线段树
count = [0]*max_len
build_tree(arr, tree, 0, 0, len(arr)-1)
#for i in range(15):
#print("tree[{}] = {}".format(i, tree[i]))
query_num = eval(input())
res = 0
for i in range(query_num):
s = input().split()
L,R = map(int,s)
L,R = L-1,R-1
#print(L)
#print(R)
res += query_tree(arr, tree, count, 0, 0, len(arr)-1, L, R)
#print(res)
#print(count)
pushdown(count,len(arr)*2-1)
#print(count)
# 求最大和
count.sort(reverse = True)
#print(count)
arr.sort(reverse = True)
res_x = 0
for i in range(len(arr)):
res_x += arr[i]*count[i]
print(res_x-res)
结果不再给出。