Codeforces H. Prime Gift(折半枚举二分)

题目描述:

Prime Gift
time limit per test
3.5 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Opposite to Grisha's nice behavior, Oleg, though he has an entire year at his disposal, didn't manage to learn how to solve number theory problems in the past year. That's why instead of Ded Moroz he was visited by his teammate Andrew, who solemnly presented him with a set of n distinct prime numbers alongside with a simple task: Oleg is to find the k-th smallest integer, such that all its prime divisors are in this set.
Input

The first line contains a single integer n (1 ≤ n ≤ 16).

The next line lists n distinct prime numbers p1, p2, ..., pn (2 ≤ pi ≤ 100) in ascending order.

The last line gives a single integer k (1 ≤ k). It is guaranteed that the k-th smallest integer such that all its prime divisors are in this set does not exceed 1018.
Output

Print a single line featuring the k-th smallest integer. It's guaranteed that the answer doesn't exceed 1018.
Examples
input

3
2 3 5
7

output

8

input

5
3 7 11 13 31
17

output

93

Note

The list of numbers with all prime divisors inside {2, 3, 5} begins as follows:

(1, 2, 3, 4, 5, 6, 8, ...)

The seventh number in this list (1-indexed) is eight.

思路:

刚开始完全处于懵逼状->这样o((⊙﹏⊙))o咱啥也不会,啥思路也没有。这是什么题,给你一个装着最多十六个素数的集合S,算出第k个数,使得这个数的全部素因子在这个集合中。第一个想法是暴力枚举,枚举出由这些数构造出的所有小于等于\(10^{18}\)的数。可第一步咋构造咱都不会(只是我不会)。后来才知道可以用dfs暴力构造集合G,像下面这样:

void dfs(int l,int r,long long val)//l,r指给的素数集合区间左右端点
{
    num.push_back(val);//num存构造的数
    for(int i = l;i<=r;i++)
    {
        if(val<=INF/a[i])//INF就是10^18
        {
            dfs(i,r,val*[i],id);//不断选择素因子相乘
        }
    }
}
dfs(1,a.size()-1,1);

还可以这样

void dfs(int idx, int64_t cur)
{
    if (idx == s.size()) {//s是存的素因子的集合
        gs.push_back(cur);//gs是构造出的数
        return;
    }
    if (s[idx] <= inf / cur)
        dfs(idx, cur * s[idx]);
    dfs(idx + 1, cur);
}

于是集合G构造好了,但是肯定超时。于是考虑折半,怎么折。把素因子集合的奇数下标元素分一组记为S1,偶数下标分一组记为S2,再分别构造出G1,G2,这样做可以减小枚举的范围(如果不分的话将会枚举一个很庞大的范围)。然后呢?我们从1到INF中二分寻找数x,使得x是素因子构造的集合中的第k个数,就找到了题目中要求的x。

那我们怎么判断一个数是原集合G中的第几个数呢?要知道,新构造出的G1,G2只是由原来的S的一半构造出来的,还缺了将两个集合“乘起来”才能得到的数。比如S={2,3,5},S1={2,5},S2={3},G1={2,4,5,8,10,...},G2={3,9,27,81,...}。却了{6,18,...},等等有两个集合"相乘"得到的数。那还怎么知道数x是G的第几个数呢?这里用到了巧妙的思想,就是把集合排好序后从一个集合G1的最大元素开始枚举G1[i],看第二个集合G2中有多少数能够小于等于\(\frac{x}{G1[i]}\),这一个操作实际上已经是在把两个集合“相乘”体现出来了,也就是看数x前面有多少个小于等于x的数,因为前面说了G就是G1"乘"G2的结果。所以最后得到的就是x本身在G中的位置。

注意的是一个技巧,在看G2中有多少个数小于等于\(\frac{x}{G1[i]}\)时由于G1[i]是从小到大的,那么上一次判断的终止G2[j]一定小于\(\frac{x}{G1[i]}\),因此G2不必每次都从0开始判断,直接接着上一次判断的结果继续累计就可以了。

还有就是二分答案的细节返回l(左端点),当然不同的写法返回的不一定是l。

代码:

#include <iostream>
#include <algorithm>
#include <vector>
#define max_n 20
#define INF  1e18
using namespace std;
int n;
int k;
int fac[max_n];
vector<long long> num[2];
vector<int> a[2];
void dfs(int l,int r,long long val,int id)
{
    num[id].push_back(val);
    for(int i = l;i<=r;i++)
    {
        if(val<=INF/a[id][i])
        {
            dfs(i,r,val*a[id][i],id);
        }
    }
}
int check(long long x)
{
    long long res = 0;
    long long j = 0;
    for(int i = num[0].size()-1;i>=0;i--)
    {
        while(j<num[1].size()&&num[1][j]<=x/num[0][i]) j++;
        res += j;
    }
    return res;
}
int main()
{
    cin >> n;
    for(int i = 0;i<n;i++)
    {
        int tmp;
        cin >> tmp;
        a[i&1].push_back(tmp);
    }
    cin >> k;
    dfs(0,a[0].size()-1,1,0);
    sort(num[0].begin(),num[0].end());
    dfs(0,a[1].size()-1,1,1);
    sort(num[1].begin(),num[1].end());
    /*for(int i = 0;i<num[0].size();i++)
    {
        cout << num[0][i] << " ";
    }
    cout << endl;
    for(int i = 0;i<num[1].size();i++)
    {
        cout << num[1][i] << " ";
    }
    cout << endl;*/
    long long l = 0;
    long long r = INF;
    long long mid;
    while(l<=r)
    {
        mid = (l+r)>>1;
        long long ord = check(mid);
        if(ord<k)
        {
            l = mid+1;
        }
        else
        {
            r = mid-1;
        }
    }
    cout << l << endl;
    return 0;
}

参考文章:

Wisdom+.+,912E - Prime Gift,https://www.cnblogs.com/widsom/p/8352859.html

SiuGinHung,Codeforces 912E - Prime Gift,https://www.cnblogs.com/siuginhung/p/8232064.html

posted @ 2019-08-11 11:35  小张人  阅读(222)  评论(0编辑  收藏  举报
分享到: