Problem Description:
Given a sequence of positive integers and another positive integer p p p. The sequence is said to be a perfect sequence if M ≤ m × p M\leq m\times p M≤m×p where M M M and m m m are the maximum and minimum numbers in the sequence, respectively.
Now given a sequence and a parameter p p p, you are supposed to find from the sequence as many numbers as possible to form a perfect subsequence.
Input Specification:
Each input file contains one test case. For each case, the first line contains two positive integers N N N and p p p, where N N N ( ≤ 1 0 5 \leq 10^5 ≤105) is the number of integers in the sequence, and p p p ( ≤ 1 0 9 \leq 10^9 ≤109) is the parameter. In the second line there are N N N positive integers, each is no greater than 1 0 9 10^9 109.
Output Specification:
For each test case, print in one line the maximum number of integers that can be chosen to form a perfect subsequence.
Sample Input:
10 8
2 3 20 4 5 1 6 7 8 9
Sample Output:
8
Problem Analysis:
算法 1(双指针 + 二分):
先将所给序列从大到小排序,然后枚举最小值 m m m,设定一个全局最大值 maxlen
,然后二分出满足要求的最大的 M M M,不断更新 maxlen
即可,时间复杂度为 O ( n log n ) O(n\log n) O(nlogn)。
算法 2(双指针):
由于可以证明在双指针过程中,当前这一轮的最大值 M M M 向右移的过程中,最小值 m m m 一定不会向左移动或者不变,并且一定也会向右移,故可以直接进行双指针,并且每一个元素最多只会被遍历一次,这种做法时间复杂度是线性的。
证明过程:
设当前枚举的最小值为 m m m,最大值为 M M M,向右移动后最大值为 M ′ M' M′, m m m 移动后为 m ′ m' m′,其中 m ′ ≤ m m' \leq m m′≤m,而又 ∵ \because ∵ M ≤ m × p , M ′ > m × p ≥ m ′ × p M\leq m\times p,\ M' > m\times p\geq m' \times p M≤m×p, M′>m×p≥m′×p,矛盾。所以 M M M 在向右移动的过程中, m m m 也会顺次向右移动。
Code
双指针 + 二分
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int n, p;
int a[N];
bool check(int min_id, int max_id)
{
if (a[max_id] <= (LL)a[min_id] * p) return true;
return false;
}
int main()
{
cin >> n >> p;
for (int i = 0; i < n; i ++ )
scanf("%d", &a[i]);
sort(a, a + n);
// 1 2 3 4 5 6 7 8 9 20
// maxa <= mina * p
int maxlen = -1;
for (int i = 0; i < n; i ++ ) // 枚举最小数,即起始位置
{
int l = i + 1, r = n - 1; // 找到第一个不满足这个条件的边界
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(i, mid)) l = mid;
else r = mid - 1;
}
int len = r - i + 1;
maxlen = max(maxlen, len);
}
cout << maxlen << endl;
return 0;
}
双指针
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int n, p;
int a[N];
int main()
{
cin >> n >> p;
for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
sort(a, a + n);
int res = 0;
for (int i = 0, j = 0; i < n; i ++ )
{
while ((long long)a[j] * p < a[i]) j ++ ; // a[j] 是 m,a[i]是 M
res = max(res, i - j + 1);
}
cout << res << endl;
return 0;
}