问题描述
给定一个数组 A 和一些查询 Li,Ri, 求数组中第 Li 至第 Ri 个元素之和。
小蓝觉得这个问题很无聊, 于是他想重新排列一下数组, 使得最终每个查 询结果的和尽可能地大。小蓝想知道相比原数组, 所有查询结果的总和最多可 以增加多少?
输入格式
输入第一行包含一个整数 n 。
第二行包含 n 个整数 A1,A2,⋯,An, 相邻两个整数之间用一个空格分隔。
第三行包含一个整数 m 表示查询的数目。
接下来 m 行, 每行包含两个整数 Li、Ri, 相邻两个整数之间用一个空格分 隔。
输出格式
输出一行包含一个整数表示答案。
样例输入
5
1 2 3 4 5
2
1 3
2 5
样例输出
4
样例说明
原来的和为 6+14=20, 重新排列为 (1,4,5,2,3) 后和为 10+14=24, 增 加了 4。
评测用例规模与约定
对于 30% 的评测用例, n,m ≤ 50;
对于 50% 的评测用例, n,m ≤ 500;
对于 70% 的评测用例, n,m ≤ 5000;
对于所有评测用例, 1 ≤ n,m ≤ 10^5,1 ≤ Ai ≤ 10^6,1 ≤ Li ≤ Ri ≤ 10^6 。
运行限制
- 最大运行时间:1s
- 最大运行内存: 512M
思路:
题目希望所有查询之和经过重新排序后增加最多,因此考虑先计算出当前的查询之和,以及排序后最大的查询之和。
由于给定查询区间,因此排序前和排序后询问的区间是一样的,那么可以利用 m 个查询区间计算出每个位置 j 的累计查询次数 s[j]
每次查询区间 [Li,Ri],相当于区间 [Li,Ri] 中的每个数字查询次数 +1。为了统计每个数字的查询次数,需要实现区间加法,可以使用线段树、树状数组、差分数组来得到数组 s 的值。
此处使用差分数组的做法实现区间更新:
- 在区间 [L,R] 上 + 1 ,只需要在差分数组 b[L]+1,b[Ri+1]−1。
- 最终 s[ i ] = s[ i-1 ] +b[ i ]。
此时最开始的查询之和相当于统计每个数字的查询次数:
sum1+=a[ i ] * s[ i ]
需要对数组 A 进行排序,使得新的 sum 最大。利用贪心的思想可以得出结论:查询次数越多,数值一定越大。这样才能保证和最大。
因此对 A 数组从小到大排序,而 s 数组也从小到大排序,使得A中最小的对应 s 中最小的,�A 中最大的对应 s 中最大的,此时可以保证和最大。
参考代码 :
n=int(input())
a=list(map(int,input().split()))
a=[0,*a] #a=[0,1,2,3,4,5]
b=[0]*(n+2) #区间加法更新
s=[0]*(n+1) #差分数组前缀和
m=int(input())
for i in range(m):
l,r=map(int,input().split())
b[l]+=1
b[r+1]-=1
for i in range(1,n+1):
s[i]=s[i-1]+b[i]
sum1=0
sum2=0
for i in range(1,n+1):
sum1+=a[i]*s[i] #原始排列和
a.sort()
s.sort()
for i in range(1,n+1):
sum2+=a[i]*s[i] #新排列和
print(sum2-sum1)
单星号 * 的用法
将所有参数以元组(tuple)的形式导入:
实例
def foo(param1, *param2):
print (param1)
print (param2)
foo(1,2,3,4,5)
以上代码输出结果为:
1 (2, 3, 4, 5)
差分数组详见http://t.csdn.cn/hiH42