Codeforces 912E

912E - Prime Gift

思路:

折半枚举+二分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;
}
原文地址:https://www.cnblogs.com/widsom/p/8352859.html