一、问题描述
试题编号: 202112-2
试题名称: 序列查询新解
时间限制: 1.0s
内存限制: 512.0MB
问题描述:
题目背景
上一题“序列查询”中说道:
是一个由 个 范围内整数组成的序列,满足
。基于序列 ,对于 范围内任意的整数 ,查询 定义为:序列 中小于等于 的整数里最大的数的下标。
对于给定的序列 和整数 ,查询 是一个很经典的问题,可以使用二分搜索在 的时间复杂度内轻松解决。但在 IT 部门讨论如何实现这一功能时,小 P 同学提出了些新的想法。
题目描述
小 P 同学认为,如果事先知道了序列 中整数的分布情况,就能直接估计出其中小于等于 的最大整数的大致位置。接着从这一估计位置开始线性查找,锁定 。如果估计得足够准确,线性查找的时间开销可能比二分查找算法更小。
比如说,如果
均匀分布在 的区间,那么就可以估算出:
为了方便计算,小 P 首先定义了比例系数
,其中 表示下取整,即 等于 除以 的商。进一步地,小 P 用
表示自己估算出的 的大小,这里同样使用了下取整来保证 是一个整数。
显然,对于任意的询问 , 和 越接近则说明小 P 的估计越准确,后续进行线性查找的时间开销也越小。因此,小 P 用两者差的绝对值 来表示处理询问 时的误差。
为了整体评估小 P 同学提出的方法在序列 上的表现,试计算:
输入格式
从标准输入读入数据。
输入的第一行包含空格分隔的两个正整数 和 。
输入的第二行包含 个用空格分隔的整数
。
注意
固定为 ,因此输入数据中不包括
。
输出格式
输出到标准输出。
仅输出一个整数,表示 的值。
样例1输入
3 10
2 5 8
Data
样例1输出
5
Data
样例1解释
样例2输入
9 10
1 2 3 4 5 6 7 8 9
Data
样例2输出
0
Data
样例3输入
2 10
1 3
Data
样例3输出
6
Data
样例3解释
二、python代码与注释
思路可以看参考文献。
n, N = tuple(map(int, input().split()))
# 有用的点
A = [0] + [int(x) for x in input().split()]
# 计算gi数列下标到x的和
def h(x):
if x <= 0:
return 0
global r
x = x+1
n = x//r
m = x%r
return int(n*(r*(n-1)/2+m))
# 对下标为left到right的abs(fi-gi)求和,包括left和right两点
def cal(fi, left, right):
return abs(h(right)-h(left-1)-fi*(right-left+1))
# 判断是否有转折点,并返回error增加量
def ischange(fi, left, right):
# 没有转折点
if left//r >= fi or right//r <= fi:
return cal(fi, left, right);
# 出现转折点
else:
# 算出第一个转折点
splitindex = left + r*(fi-(left+1)//r)+r-(left+1)%r
return cal(fi, left, splitindex)+ischange(fi, splitindex+1, right)
r = N//(n+1)
error = 0
# 根据fi的数值来分组,开始遍历
for fi in range(n+1):
left = A[fi]
if fi < n:
right = A[fi+1]-1
else:
right = N-1
error += ischange(fi, left, right)
print(error)
总结
对于数据有规律的题目来说,一般要利用其规律,而不能直接一个个求解。(要利用数列求和的公式等)
求差分的时候可以转化成两个前缀和函数的差(若前缀和函数好求的话)
写代码时要注意区分好变量意义,是下标还是数量(特别是在计算其他下标位置时)