如果 M≤m×p成立,其中 M和m分别是序列中的最大和最小数,则该序列被称为完美序列。
可知一个序列是否是完美序列只与该序列中的最小值和最大值有关,与该序列的其他元素是无关的。
题目要求从序列中找到尽可能多的数字以构成一个完美的子序列,注意该题的子序列可以是不连续的,因此如果要把所有的子序列都枚举出来要$2^n$次,必然超时。
因为一个序列是否是完美序列只与该序列中的最小值和最大值有关,与该序列的其他元素是无关的,因此可以往枚举最小值最大值这个思路想。并且题目要求找到尽可能多的数字以构成一个完美的子序列,所以最小值和最大值之间的元素应该都选上,由这个分析可知,应该要对原序列进行排序,排序后,每次选择一个最小值一个最大值,就相当于问题转换为每次选择一个区间,这个问题的最优解一定就是排好序之后的某一个区间长度(这个区间里面的元素都要选上作为子序列,这样可以保证尽可能多的数字)
因此,原问题就转换为了对原序列排序,然后枚举区间,求一个最大区间的序列满足完美序列。枚举的话可以先枚举最大值a[i]
(即从右往左枚举区间右端点),然后对每个最大值找到满足a[i] ≤ a[j] × p
的最小下标j
(在本题中可以二分查找,也可以双指针),对于每个最大值a[i]
一定能找到其对应满足a[i] ≤ a[j] × p
的最小下标j
,因为区间为1也是一个完美序列,即i=j
。
最所有枚举的情况的区间长度取max,最后的ans就是答案
二分:
能不能使用二分的依据就是在一个区间满足这个性质,在另一个区间不满足这个性质
在本题中左侧区间满足a[i] > a[j] * p
,在右侧区间满足a[i] <= a[j] * p
,即不满足a[i] > a[j] * p
,所以可以二分
def find(l,r):
M = a[r]
# 找满足M <= a[mid] * p的左边界
while l < r:
mid = l + r >> 1
if M <= a[mid] * p:
r = mid
else:
l = mid + 1
return l
n,p = map(int,input().split())
a = list(map(int,input().split()))
a.sort() # 对原序列排序
ans = 0
for i in range(n - 1,-1,-1):
j = find(0,i) # 二分查找满足a[i] ≤ a[j] × p的最小下标j
if i - j + 1 > ans:
ans = max(ans,i - j + 1)
print(ans)
双指针算法:
- i指向最大的数,即M
- j指向最小的数,即m。更精确的定义j为离当前的i,j往左最远能到什么地方,使
a[i] <= a[j] * p
。因为j是有单调性的:i往右走,j是不可能往左走的。所以该算法的时间复杂度是线性的,i和j指针都是从前移到后,也就是计算2n次,$O(2n)$的时间复杂度。 - 如果不符合
a[i] <= a[j] * p
,则向右移动j指针,直至符合条件为止
n,p = map(int,input().split())
a = list(map(int,input().split()))
a.sort()
ans = 0
j = 0
for i in range(n):
while j <= i and a[i] > a[j] * p:
j += 1
ans = max(ans,i - j + 1)
print(ans)