思路:
折半枚举+二分check
将素数分成两个集合(最好按奇偶位置来,保证两集合个数相近),这样每个集合枚举出来的小于1e18的积个数小于1e6。
然后二分答案,check时枚举其中一个集合,然后找到另外一个集合小于mid/该元素的元素有多少个,这里用到一个双指针的技巧将复杂度降到O(n):一个集合从大到小枚举,那么mid/该元素就会逐渐变大,那么直接从上次找到的位置往后找就可以了,因为答案肯定在后面。
代码:
#include<bits/stdc++.h> using namespace std; #define ll long long #define pb push_back #define mem(a,b) memset(a,b,sizeof(a)) const ll INF=1e18; vector<ll>s[2]; int p[20],a[20]; ll k; void dfs(int l,int r,ll v,int id){ s[id].pb(v); for(int i=l;i<=r;i++){ if(INF/v>=p[i])dfs(i,r,v*p[i],id); } } bool check(ll a){ ll cnt=0; int j=0; for(int i=s[0].size()-1;i>=0;i--){ while(j<s[1].size()&&a/s[0][i]>=s[1][j])j++; cnt+=j; } return cnt<k; } int main(){ ios::sync_with_stdio(false); cin.tie(0); int n; cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=n/2;i++)p[i]=a[i*2]; for(int i=n/2+1;i<=n;i++)p[i]=a[(i-n/2)*2-1]; cin>>k; int hf=n/2; int _hf=n-hf; dfs(1,hf,1,0); dfs(hf+1,n,1,1); sort(s[0].begin(),s[0].end()); sort(s[1].begin(),s[1].end()); ll l=1,r=1e18,mid=(l+r)>>1; while(l<r){ if(check(mid))l=mid+1; else r=mid; mid=(l+r)>>1; //cout<<l<<‘ ‘<<r<<endl; } cout<<mid<<endl; return 0; }