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; }