传送门
// 题意: 求第x小的正整数v使得其最小的质因数为质数y,即正好有x-1个[1,v-1]之内的正整数满足其最小的质因数为质数y
若答案超过1000000000则输出0
// 思路: 其实如果范围不大, 我们都可以通过筛法处理. 但是由于数比较大, 也就是当素数因子比较小的时候变不再适用. 方法就是当 y >= p, 可以用1e9/y的筛法处理.
y < p , 就二分答案加容斥来处理
实际上的操作就是类似于筛法, 所以我们check二分答案时, 将小于y的所有的素因子的倍数用容斥从mid中删掉, 那么剩下的就是y是其最小质因数的那些数, 所以我们就可以不断用二分逼近答案, 这就是原理.
AC Code
const int maxn = 1e5+5;
int bound = 1e9;
int a[maxn];
ll x,y;
bool ok[20000000];
void work1() {
for (int i = 2 ; i < y; i++) {
if (!ok[i]) {
for (int j = i ; j <= bound / y ; j += i) {
ok[j] = 1;
}
}
}
int cnt = 0;
for (int i = 1 ; i <= bound / y ; i++){
if (!ok[i]) {
cnt++;
if (cnt == x) {
cout << i*y << endl;
return ;
}
}
}
cout << 0 << endl;
}
vector<int>ve;
ll rr(ll u) { // 做容斥
ll sum = 0 ;
for (int i = 1 ; i < (1 << ve.size()) ; i++) {
ll tmp = 1, cnt = 0 ; int flag = 0;
for(ll j = 0;j < ve.size() ; j++) {
if(i & (1<if (tmp > u) { // 超过的没有意义.
flag = 1;
break;
}
cnt++;
}
}
if(!flag) {
if (cnt & 1) sum += u / tmp;
else sum -= u / tmp;
}
}
return u - sum;
}
void work2() {
ve.clear();
for (int i = 2 ; i < y ; i++) { //选好素因子
int flag = 0;
for (int j = 2 ; j*j <= i ; j++) {
if (i % j == 0){
flag = 1;
break;
}
}
if (!flag) ve.pb(i);
}
ll l = 1, r = bound, mid, ans = 1;
while(r >= l) {
mid = (l + r) >> 1;
ll tmp = rr(mid);
if ( tmp >= x) {
r = mid - 1;
ans = mid;
}
else l = mid + 1;
}
if (ans * y > bound) cout << 0 << endl;
else cout << ans*y << endl;
}
void solve()
{
while(cin >> x >> y) {
if (y > 61) work1();
else work2();
}
}