[蓝桥杯2022初赛] 重新排序

题目:

来源:

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)

结果不再给出。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值