Codeforces Round #456 (Div. 2) 912E E. Prime Gift
题
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 */