step1: エストラテネスで までの素数を列挙します。 以下の素数の個数を とします。
step2.「小さい方から 番目」を言い換えると、「 ある数 以下で と表せる数がちょうど 個 ある」となります。
整数 以下で と表せる数の個数
とします。 は単調増大であるので答えは となる最小の となり、二分探索で求めることができます。
step3. を高速に求める方法を考えます。
を固定し、条件を満たす の数を求めます。以下の式で を で求めることが出来ます。
以上の整数について、素因数分解は一意に定まるので、 を入れ替えたケースを除いて、重複を考える必要はありません。すなわち とすることで簡単に数え上げることが出来ます。
上図より、 を定めた時の の個数は、 を満たす で、 、それ以外で です。
二分探索を使い、以下の式で を で求めることが出来ます。
足し合わせる の範囲はを満たす とし、
です。
のうち、 以上の要素の個数
のうち、 以下の要素の個数
以下の最大の素数を とすると、計算量は です。
from bisect import *
def prime_L(n):
is_prime = [True] * (n + 1)
is_prime[0] = False
is_prime[1] = False
for i in range(2, int(n**0.5) + 1):
if not is_prime[i]:
continue
for j in range(i * 2, n + 1, i):
is_prime[j] = False
return [i for i in range(n + 1) if is_prime[i]]
def idx_le(A, x): # x 以下の最大の要素位置 / なければ "No"
return bisect_right(A, x)-1 if bisect_right(A, x)-1 != -1 else "No"
def idx_lt(A, x): # x 未満の最大の要素位置 / なければ "No"
return bisect_left(A, x)-1 if bisect_right(A, x)-1 != -1 else "No"
def idx_ge(A, x): # x 以上の最小の要素位置 / なければ "No"
return bisect_left(A, x) if bisect_left(A, x) != len(A) else "No"
def idx_gt(A, x): # x 超過の最小の要素位置 / なければ "No"
return bisect_right(A, x) if bisect_right(A, x) != len(A) else "No"
def cnt_le(A, x): # x 以下の要素の個数
if(idx_le(A, x) == "No"): return 0
return idx_le(A, x) + 1
def cnt_lt(A, x): # x 未満の要素の個数
if(idx_lt(A, x) == "No"): return 0
return idx_lt(A, x) + 1
def cnt_ge(A, x): # x 以上の要素の個数
return len(A) - cnt_lt(A, x)
def cnt_gt(A, x): # x 超過の要素の個数
return len(A) - cnt_le(A, x)
INF = 10**12
def f(n):
cnt = 0
for i in range(l):
if(L[i] > n//L[i]): break
cnt += cnt_ge(L,L[i])+cnt_le(L,n//L[i])-l
return cnt
k,x = map(int,input().split())
L = prime_L(k)
l = len(L)
ng = -1
ok = INF
while abs(ok-ng)>1:
mid = (ok+ng)//2
if(f(mid)>=x):
ok = mid
else:
ng = mid
print(ok)二分探索と尺取り法を用いた解法
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
ll k, x;
cin >> k >> x;
vector<ll> primes;
vector<ll> lpf(k + 1, 0);
for(ll i = 2; i <= k; i++){
if(lpf[i] == 0){
lpf[i] = i;
primes.push_back(i);
}
for(auto p: primes){
if(p * i > k || p > lpf[i]) break;
lpf[p * i] = p;
}
}
ll n = primes.size();
auto f = [&](ll mid) -> ll {
ll res = 0;
int ok = n - 1;
for(int i = 0; i < n; i++){
while(ok >= 0 && primes[ok] * primes[i] > mid) ok--;
res += min(i + 1, ok + 1);
}
return res;
};
ll ng = 1, ok = k * k;
while(ok - ng > 1){
ll mid = (ok + ng) / 2;
if(f(mid) < x) ng = mid;
else ok = mid;
}
cout << ok << endl;
}