codeforces912E(折半搜尋+雙指標+二分答案)
E. Prime Gift
E. Prime Gift time limit per test 3.5 seconds memory limit per test 256 megabytes input standard input output standard outputOpposite to Grisha's nice behavior, Oleg, though he has an entire year at his disposal, didn't manage to learn how to solve number theory problems in the past year. That's why instead of Ded Moroz he was visited by his teammate Andrew, who solemnly presented him with a set of n
The first line contains a single integer n (1 ≤ n ≤ 16).
The next line lists n distinct prime numbers p
The last line gives a single integer k (1 ≤ k). It is guaranteed that the k-th smallest integer such that all its prime divisors are in this set does not exceed 1018.
OutputPrint a single line featuring the k
3output Copy
2 3 5
7
8
input
Copy
5output Copy
3 7 11 13 31
17
93
Note
The list of numbers with all prime divisors inside {2, 3, 5} begins as follows:
(1, 2, 3, 4, 5, 6, 8, ...)
The seventh number in this list (1-indexed) is eight.
/*
給定一個大小為n的素數集合
求出分解後只含這些質數因子的第k小整數
直接列舉判斷顯然不可以。
考慮折半搜尋。可以把這16個數字拆成2個子集,各自生成所有大小1e18及以下的積。
但也需要使兩個乘積組成的集合儘量接近。可以預先造出極限資料試一試集合裡能有多少數
對於最壞情況,即如下資料
16
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53
分為2 3 5 7 11 13 和 17 19 23 29 31 37 41 43 47 53 兩個集合時
這兩個集合生成的1e18及以下的積的數量分別為 958460個 和 505756個,並不大
集合中數必須兩兩不等。
最後統計答案,
兩個集合生成的積各自排一下序
然後二分答案,對於每個答案 u,可以O(|S|)雙指標得到他是第幾大。
具體做法是列舉從到小列舉第一個集合的積 t1,然後計算一下第二個集合的積中有多少積和 t1 相乘小於等於 u,
由於是從大到小列舉的,所以t1必然遞增所以第二個集合的積中符合條件的積的數量也必然是遞增的,所以只要掃一遍就行。
*/
#include<bits/stdc++.h>
#define ll long long
#define inf 1e18
#define N 24
using namespace std;
vector<ll> seg[2];
int p[N],n;
ll ansid;
void dfs(int L,int R,ll val,int id)
{
seg[id].push_back(val);
for(int i=L;i<=R;i++)
if(inf/p[i]>=val) dfs(i,R,val*p[i],id);
}
ll cnt(ll num)
{
int j=0;
ll ret=0;
for(int i=seg[0].size()-1;i>=0;i--)
{
while(j<seg[1].size() && seg[1][j]<=num/seg[0][i])
j++;
ret+=j;
}
return ret;
}
void solve()
{
int i,j;
dfs(1,min(6,n),1,0);
dfs(min(6,n)+1,n,1,1);
sort(seg[0].begin(),seg[0].end());
sort(seg[1].begin(),seg[1].end());
ll L=0,R=inf,mid;
while(L<R-1)
{
mid=(L+R)>>1;
if(cnt(mid)>=ansid) R=mid;
else L=mid;
}
cout<<R<<endl;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&p[i]);
cin>>ansid;
solve();
return 0;
}