题目链接:Prime Gift
题意:
给出了n(1<=n<=16)个互不相同的质数pi(2<=pi<=100),现在要求第k大个约数全在所给质数集的数。(保证这个数不超过1e18)
题解:
如果暴力dfs的话肯定超时间,其实给的n数据范围最大是16是一个很奇妙的数(一般折半枚举基本上是这样的数据范围@。@~)。所以想到折半枚举,把所有的质数分成两份求出每份中所有小于1e18的满足条件的数。然后二分答案,写cheak函数时遍历第一个集合,对第二个集合二分(折半枚举基本上这个套路)。但是,这里一定要注意的是这里折半枚举指的并不是将n分成两份求出两个集合,而是要让分出的两份数所形成的集合中所含的个数接近,因为用较小的数形成的集合个数要多很多(被这个点卡了好久#。#)。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAX_N = 1e4+9; 4 const long long INF = 1e18; 5 long long N,M,T,k; 6 long long vec[2][MAX_N]; 7 vector<long long> st[2]; 8 void dfs(int l,int r,long long rt,int id) 9 { 10 st[id].push_back(rt); 11 for(int i=l;i<r;i++) 12 { 13 if(INF/vec[id][i] >= rt) 14 dfs(i,r,rt*vec[id][i],id); 15 } 16 } 17 long long cheak_num(long long x) 18 { 19 long long ans = 0; 20 for(int i=0;i<st[0].size();i++) 21 { 22 if(st[0][i] ) 23 ans += upper_bound(st[1].begin(),st[1].end(),x/st[0][i]) - st[1].begin(); 24 } 25 return ans; 26 } 27 int main() 28 { 29 while(cin>>N) 30 { 31 st[0].clear();st[1].clear(); 32 for(int i=0;i<min((long long)5,N);i++) scanf("%lld",&vec[0][i]); 33 for(int i=0;i<N-min((long long)5,N);i++) scanf("%lld",&vec[1][i]); 34 dfs(0,min((long long)5,N),1,0); 35 dfs(0,N-min((long long)5,N),1,1); 36 for(int i=0;i<2;i++) 37 sort(st[i].begin(),st[i].end()); 38 39 cin>>k; 40 long long l=1,r=INF; 41 while(l<=r) 42 { 43 long long mid = (l+r)>>1; 44 if(cheak_num(mid) < k) l = mid+1; 45 else r = mid-1; 46 } 47 cout<<l<<endl; 48 } 49 return 0; 50 } 51 /* 52 16 53 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 54 755104793 55 */