例题1
1010 只包含因子2 3 5的数
http://www.51nod.com/Challenge/Problem.html#problemId=1010
K的因子中只包含2 3 5。满足条件的前10个数是:2,3,4,5,6,8,9,10,12,15。
所有这样的K组成了一个序列S,现在给出一个数n,求S中 >= 给定数的最小的数。
例如:n = 13,S中 >= 13的最小的数是15,所以输出15。
输入
第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 10000) 第2 - T + 1行:每行1个数N(1 <= N <= 10^18)
输出
共T行,每行1个数,输出>= n的最小的只包含因子2 3 5的数。
输入样例
5
1
8
13
35
77
输出样例
2 8 15 36 80
题解 :这个题目用到二分,二分之前需要预处理一个数组
定理:K=2^x*3^y*5^z即k是只包含2,3,5,的因子的数
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll inf =1e18+10000;//这里的范围要比1e8大1000左右 const int N=1E6+7; ll arr[N]; int pos=0; void inint(){ ll i, j, k; for(i=1;i<inf;i*=2) for(j=1;j*i<inf;j*=3) for(k=1;k*i*j<inf;k*=5) arr[pos++]=i*j*k; } int main(){ inint(); sort(arr,arr+pos); int t; scanf("%d",&t); while(t--){ ll n; cin>>n; ll ans; int left=1; int right=pos-1; while(left<=right){ int mid=(left+right)/2; if(arr[mid]>=n){ ans=arr[mid]; right=mid-1; } else { left=mid+1; } } cout<<ans<<endl; } return 0; }
例题2
1267 4个数和为0
http://www.51nod.com/Challenge/Problem.html#problemId=1267
给出N个整数,你来判断一下是否能够选出4个数,他们的和为0,可以则输出"Yes",否则输出"No"。
输入
第1行,1个数N,N为数组的长度(4 <= N <= 1000) 第2 - N + 1行:A[i](-10^9 <= A[i] <= 10^9)
输出
如果可以选出4个数,使得他们的和为0,则输出"Yes",否则输出"No"。
输入样例
5
-1
1
-5
2
4
输出样例
Yes
题解:要找4个数,这四个数的和为0,我们先确定好两个数,然后再二分查找另外两个数
#include<bits/stdc++.h> using namespace std; const int N=1000+7; int arr[N]; int main(){ int t; cin>>t; for(int i=0;i<t;i++) scanf("%d",&arr[i]); sort(arr,arr+t); bool flag=false ; for(int i=0;i<t;i++){ for(int j=i+1;j<t;j++){ for(int k=j+1;k<t;k++){ int ans=arr[i]+arr[j]+arr[k]; int left=k+1; int right=t-1; while(left<=right){ int mid=(left+right)/2; if(arr[mid]+ans>0){ right=mid-1; } else if(arr[mid]+ans<0){ left=left+1; } else { flag=true; break; } } if(flag) break; } if(flag) break; } if(flag) break; } if(flag) puts("Yes"); else puts("No"); return 0; }
例题3
1105 第K大的数
http://www.51nod.com/Challenge/Problem.html#problemId=1105
数组A和数组B,里面都有n个整数。
数组C共有n^2个整数,分别是:
A[0] * B[0],A[0] * B[1] ...... A[0] * B[n-1]
A[1] * B[0],A[1] * B[1] ...... A[1] * B[n-1]
......
A[n - 1] * B[0],A[n - 1] * B[1] ...... A[n - 1] * B[n - 1]
是数组A同数组B的组合,求数组C中第K大的数。
例如:
A:1 2 3,B:2 3 4。
A与B组合成的C为
A[0] A[1] A[2]
B[0] 2 3 4
B[1] 4 6 8
B[2] 6 9 12
共9个数。
输入
第1行:2个数N和K,中间用空格分隔。N为数组的长度,K对应第K大的数。(2 <= N <= 50000,1 <= K <= 10^9) 第2 - N + 1行:每行2个数,分别是A[i]和B[i]。(1 <= A[i],B[i] <= 10^9)
输出
输出第K大的数。
输入样例
3 2
1 2
2 3
3 4
输出样例
9
题解 二分套二分。传统的二分都是对排好序的数组的下标进行处理,这里不一样,我们二分的left为最小值,right为最大值,然后二分这个区间,同时判断大于mid的个数,当大于mid的个数为m-1时,就是答案。但是不一定是最优的答案,所以要继续增大,继续查找
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N= 50000+7; ll arr1[N],arr2[N]; int n,m; ll sum(ll x){ ll sum1=0; for(int i=0;i<n;i++){ int left=0; ll ans=n; int right=n-1; while(left<=right){ int mid=(left+right)/2; if(arr1[i]*arr2[mid]>x){ right=mid-1; ans=mid; } else { left=mid+1; } } sum1+=n-ans; } return sum1; } int main(){ cin>>n>>m; for(int i=0;i<n;i++) scanf("%lld%lld",&arr1[i],&arr2[i]); sort(arr1,arr1+n); sort(arr2,arr2+n); ll left=arr1[0]*arr2[0]; ll ans=0; ll right=(1ll*arr1[n-1])*(1ll*arr2[n-1]); while(left<=right){ ll mid=(left+right)/2; ll x=sum(mid); // if(x==m-1){ // ans=mid; // right=mid-1; // } //这里这样写有点不对,因为我们的sum函数只是保存了比mid大的数的个数,但是当出现两个相等的数时,比他们大的数的数目是一样的,但是这两个数的位置不一样,一个在前边,一个在后边 if(x<m){ ans=mid; right=mid-1; } else { left=mid+1; } } cout<<ans<<endl; return 0; }