当前位置: 代码迷 >> 综合 >> 【CodeForces】【中途相遇法】【二分答案】912E Prime Gift
  详细解决方案

【CodeForces】【中途相遇法】【二分答案】912E Prime Gift

热度:31   发布时间:2023-11-21 06:39:52.0

CodeForces 912E Prime Gift

题目大意

给定有NNN个素数的集合(1≤N≤161\le N\le 161N16),要求用集合内的素数通过乘法构造出一些数字,并输出这些数字中的第KKK大。

注意一个素数可以多次使用,且答案不超过101810^{18}1018

分析

注意到N≤16N\le 16N16,这意味着我们可以采用爆搜。

但直接搜索似乎不太好做。考虑中途相遇法:

我们将整个集合划分为两部分,对于集合中的素数爆搜出它们能够组合出的所有数。

然后考虑答案求解。发现直接得到第KKK大的数似乎有点困难,于是考虑二分。

这样一来问题就转化为了给定一个数和两个数列,求这个数在两个序列中任选两个数乘起来得到的序列中的排名。

这个东西用双指针做一做就可以了。

注意不能直接将序列中的两个数乘起来,我们必须将乘法转化为除法。

参考代码

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;typedef long long ll;
const int Maxn = 16;
const ll INF = 1e18;int N, p[Maxn + 5];
ll K;vector<ll> val[2];
void DFS(int typ, int x, ll now) {
    val[typ].push_back(now);for(int i = x; i <= N; i += 2)if(INF / p[i] >= now) DFS(typ, i, now * p[i]);
}bool check(ll x) {
    ll rnk = 0;for(int i = (int)val[0].size() - 1, j = 0; i >= 0; i--) {
    while(j < (int)val[1].size() && val[1][j] <= x / val[0][i])++j;rnk += j;}return rnk >= K;
}int main() {
    
#ifdef LOACLfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);
#endifscanf("%d", &N);for(int i = 1; i <= N; i++)scanf("%d", &p[i]);scanf("%lld", &K);sort(p + 1, p + N + 1);DFS(0, 1, 1);DFS(1, 2, 1);sort(val[0].begin(), val[0].end());sort(val[1].begin(), val[1].end());ll lb = 1, ub = INF;ll ans;while(lb <= ub) {
    ll mid = (lb + ub) >> 1;if(check(mid)) ub = mid - 1, ans = mid;else lb = mid + 1;}printf("%lld\n", ans);return 0;
}
  相关解决方案