完美數列
題面:
給定一個正整數數列,和正整數 p,設這個數列中的最大值是 M,最小值是 m,如果 M≤mp,則稱這個數列是完美數列。
現在給定參數 p 和一些正整數,請你從中選擇儘可能多的數構成一個完美數列。
輸入第一行給出兩個正整數 N 和 p,其中 N(≤105)是輸入的正整數的個數,p(≤109)是給定的參數。第二行給出 N 個正整數,每個數不超過 109。
在一行中輸出最多可以選擇多少個數可以用它們組成一個完美數列。
sample input:
10 8
2 3 20 4 5 1 6 7 8 9
sample output:
8
思路:
- 因爲考慮到選出序列的最大值和最小值,所以首先將輸入的序列進行排序,方便之後的處理
- 在一個給定的遞增序列中找出左邊界和右邊界,保證滿足左端點值不超過右端點值*p,並且滿足右端點與左端點的距離最大
- 可以使用二重循環進行枚舉,但是給定的參數的值比較大,如果使用循環會爆掉,同時也要注意,使用int也會超,要用long long 來表示乘法之後的結果
- 使用二分查找來降低時間複雜度,由於是遞增序列,所以如果改位置的值比預想的值小,說明下一次要在mid的後邊進行查找,如果比預想值大,那就在mid的前半段進行查找
- 代碼中的search函數就是尋找符合要求的值的位置的函數,也可以使用upper_bound函數來代替,這樣可以減少代碼量
AC代碼
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
int a[100001];
int n;
int search(int i,long long x)//對於數組尋找第一個大於x的位置
{
int l=i+1,r=n-1,mid=0;
if(a[n-1]<=x) return n;//所有數都不大於
while(l<r)
{
mid=(l+r)/2;
if(a[mid]<=x)//說明大於mid的肯定在右邊/後邊
l=mid+1;
else r=mid;
}
return l;
}
int main()
{
int p;
cin>>n>>p;
for(int i=0;i<n;i++)
cin>>a[i];
sort(a,a+n);
int ans=1;
for(int i=0;i<n;i++)
{
//查找第一個超過a[i]*p的數,返回他的位置
int pos=search(i,(long long)a[i]*p);
ans=max(ans,pos-i);
}
cout<<ans<<endl;
return 0;
}