题
OvO http://codeforces.com/contest/912/problem/E
解
首先把这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个,并不大,而且两个集合中的积必定是两两不相等的(除了1)。
记两个集合大小的和为 |S|
两个集合生成的积各自排一下序
然后二分答案,对于每个答案 u,可以 O(|S|) 得到他是第几大。
具体做法是枚举从到小枚举第一个集合的积 t1,然后计算一下第二个集合的积中有多少积和 t1 相乘小于等于 u,
由于是从大到小枚举的,所以 t1 必然递增,所以第二个集合的积中符合条件的积的数量也必然是递增的,所以只要扫一遍就行。
然后要注意的是直接用 long long 来进行计算比较好,double的精度貌似不太够
(参考自这里->http://codeforces.com/contest/912/submission/33938779)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
const ll INF=1e18;
const int N=24;
vector<ll> seg[2];
int p[N],n;
ll ansid;
void dfs(int li,int ri,ll val,int id)
{
seg[id].push_back(val);
for(int i=li;i<=ri;i++)
if(INF/p[i]>=val)
dfs(i,ri,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());
// cout<<seg[0].size()<<' '<<seg[1].size()<<endl;
ll li=0,ri=INF,mid;
while(li<ri-1)
{
mid=(li+ri)>>1;
if(cnt(mid)>=ansid)
ri=mid;
else li=mid;
}
printf("%I64d\n",ri);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&p[i]);
scanf("%I64d",&ansid);
solve();
return 0;
}
/*
16
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53
2
*/