说在前面
me怎么就没想到数据分治呢
明明两种方法都分别想到了= =
题目
题目大意
给定一个正整数N和一个质数P
请求出最小质因子为P的,第N大的数(即,有N-1个比待求数小的,最小质因子为P的数)
如果待求数大于1e9,输出0
输入输出格式
输入格式:
输入仅一行两个整数,N和P
输出格式:
输出一个数字,表示答案
解法
思考一下这个题
很容易发现,要求的数字一定是形如这样的:
kP
k
P
,其中
K=1
K
=
1
或者
K≥P,K is prime
K
≥
P
,
K
i
s
p
r
i
m
e
那么一种正确的做法就是,把所有不超过
109P
10
9
P
的数中,最小质因子小于
P
P
的那些除掉,然后选第 个与
P
P
相乘
然而如果 太小了,
109P
10
9
P
就会很大,然后就筛不完的,要么MLE要么TLE
所以直接筛是不太现实的,我们考虑一下其他做法
统计最小质因子大于等于P的数的个数,我们还可以用容斥原理,但是质数一多就不可做了
这貌似就不可做了= =?
然后就发现me实力犯蠢了一波
我们完全可以用容斥来做
P
P
比较小的情况。二分答案,然后容斥出不超过 的数里面,有多少个的最小质因子不小于
P
P
就好了
然后用埃氏筛来做 较大的部分
然后就完了
下面是自带大常数的代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
int N , P , p[100] , pcnt ;
bool vis[14500005] ;
void getP(){
for( int i = 2 ; i < P ; i ++ ){
if( !vis[i] ) p[++pcnt] = i ;
for( int j = 1 ; j <= pcnt && p[j] * i < P ; j ++ ){
vis[ i*p[j] ] = true ;
if( i % p[j] == 0 ) break ;
}
}
}
int num_cnt , mid , UP ;
void dfs( int dep , int now , short fix ){
if( !dep ){
num_cnt += fix * UP / now ;
return ;
} dfs( dep - 1 , now , fix ) ;
if( 1LL * now * p[dep] <= UP )
dfs( dep - 1 , now * p[dep] , -fix ) ;
}
void solve1(){
getP() ;
int lf = P , rg = 1e9 , ans = 0 ;
while( lf <= rg ){
int mid = ( lf + rg ) >> 1 ;
num_cnt = 0 , UP = mid / P ;
dfs( pcnt , 1 , 1 ) ;
if( num_cnt >= N ) ans = mid , rg = mid - 1 ;
else lf = mid + 1 ;
} printf( "%d" , ans ) ;
}
void solve2(){
int lim = 1e9 / P , rem = 0 ;
register int i , j ;
for( i = 2 ; i < P ; i ++ ) if( !vis[i] ){
for( j = i * i ; j <= lim ; j += i )
vis[j] = true ;
} N -- ;
for( i = P ; i <= lim ; i ++ ){
if( !vis[i] ){
N -- ;
if( !N ){ rem = i ; break ;}
}
} printf( "%d" , rem * P ) ;
}
int main(){
scanf( "%d%d" , &N , &P ) ;
if( N == 1 ){ printf( "%d" , P ) ; return 0 ; }
if( P <= 79 ) solve1() ;
else solve2() ;
}