Codeforces 912E Prime Gift 搜索,meet-in-the-middle

E. 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.



有一个素数集合,要求找到第k小的数字,满足将它分解质因数之后的所有因子都在这个素数集合里。


把这些集合中的素数分成两组,分别保存每组当中的素数互相组合相乘的结果。这里分组很有技巧性,由于2,3,5,7,11,13这前六个数组成的结果远多于其他数所组成的结果数量,所以我们取前六个为一组,后面的所有数为1组。这样,每组有不超过1e6个数。

接下来就是meet-in-the-middle了。二分答案mid,验证当前答案的大小排名,只需要找有多少个数比它小。把这个数mid分解成两组数的乘积:mid>=x*y,固定其中一个数x,在另外一个排序好的序列中查找小于等于mid/y的数的个数,再把对应所有x的满足条件的数量累加就可以得到排名。

其实还可以双指针优化,不需要每次都二分。不过这样也能卡过(3354ms)


#include <cstdio>
#include <iostream>
#include <string.h>
#include <string> 
#include <unordered_map>
#include <queue>
#include <deque>
#include <vector>
#include <set>
#include <algorithm>
#include <math.h>
#include <cmath>
#include <stack>
#include <iomanip>
#define mem0(a) memset(a,0,sizeof(a))
#define meminf(a) memset(a,0x3f,sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
const int maxn=1000005,inf=0x3f3f3f3f;  
const ll llinf=0x3f3f3f3f3f3f3f3f;   
ll a[maxn],b[maxn],c[25]; 
int na = 0, nb = 0;

void dfs(int l, int r, int num, ll now) {
	if (num == 0) a[++na] = now; else b[++nb] = now;
	for (int i = l; i <= r; i++) {
		if (c[i] <= 1000000000000000000/now) {
			dfs(i, r, num, now*c[i]);
		}
	}
}

bool check(ll mid,ll k) {
	ll sum=0;
	for (int i = 1; i <= na; i++) {
		if (a[i] > mid) continue;
		int pos = upper_bound(b + 1, b + nb + 1, mid / a[i]) - b - 1;
		sum += (ll)pos;
	}
	if (sum >= k) return true; else return false;
}

int main() {
	ll n,k,i;
	scanf("%I64d",&n);
	for (i=1;i<=n;i++) {
		scanf("%I64u",&c[i]);
	}
	scanf("%I64d",&k);
	dfs(1, min(6ll, n-1), 0, 1);
	dfs(min(6ll, n-1)+1, n, 1, 1);
	sort(b + 1, b + nb + 1);
	ll ans = 1, l, r;
	l = 1; r = 1e18+5;
	while (l <= r) {
		ll mid = (l + r) / 2;
		if (check(mid,k)) ans = mid, r = mid - 1; else l = mid + 1;
	}
	cout << ans << endl;
//	system("pause");
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值