给你一个长度为n的集合,如果一个数满足它所有的质因子都在这个集合中,那么这个数就是合法的
求第k小的合法数(1<=n<=16),答案不会超过1e18
思路:
将n个数对半拆成两个集合,每个集合元素不会超过8个,对于每个集合爆搜出1e18范围内所有的合法数
之后查询第k大时二分答案ans,可以用双指针求出当前ans是第几个合法数
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
int n;
long long p[2][25];
int cnt[2];
long long k;
long long com[2][10000005];
int cntc[2];
long long inf=1e18;
void get(int f,int id,long long cur)
{
if(id==cnt[f])
{
com[f][cntc[f]]=cur;
cntc[f]++;
}
else
{
long long ans=1;
while(cur<=inf/ans)
{
get(f,id+1,cur*ans);
if(ans<=inf/p[f][id])
ans*=p[f][id];
else
break;
}
}
}
long long check(long long x)
{
int id1=0;
int id2=cntc[1]-1;
long long ans=0;
while(id1<cntc[0])
{
while(id2!=-1 && com[0][id1]>x/com[1][id2])
{
id2--;
}
ans+=(id2+1);
id1++;
}
return ans;
}
int main() {
while(~scanf("%d",&n))
{
memset(cnt,0,sizeof(cnt));
memset(cntc,0,sizeof(cntc));
for(int i=0;i<n;i++)
{
int id=i%2;
long long tp;
scanf("%lld",&tp);
p[id][cnt[id]]=tp;
cnt[id]++;
}
scanf("%lld",&k);
get(0,0,1);
get(1,0,1);
sort(com[0],com[0]+cntc[0]);
sort(com[1],com[1]+cntc[1]);
long long l=1,r=1e18;
while(l<r)
{
long long mid=(l+r)/2;
long long tc=check(mid);
if(tc<k)
{
l=mid+1;
}
else if(tc>k)
{
r=mid-1;
} else
{
r=mid;
}
}
printf("%lld\n",r);
}
return 0;
}