[BZOJ3181]-[Coci2012]BROJ-数据分治

说在前面

me怎么就没想到数据分治呢
明明两种方法都分别想到了= =


题目

BZOJ3181传送门

题目大意

给定一个正整数N和一个质数P
请求出最小质因子为P的,第N大的数(即,有N-1个比待求数小的,最小质因子为P的数)
如果待求数大于1e9,输出0

输入输出格式

输入格式:
输入仅一行两个整数,N和P

输出格式:
输出一个数字,表示答案


解法

思考一下这个题
很容易发现,要求的数字一定是形如这样的: kP k P ,其中 K=1 K = 1 或者 KP,K is prime K ≥ P , K   i s   p r i m e
那么一种正确的做法就是,把所有不超过 109P 10 9 P 的数中,最小质因子小于 P P 的那些除掉,然后选第 N 个与 P P 相乘
然而如果 P 太小了, 109P 10 9 P 就会很大,然后就筛不完的,要么MLE要么TLE

所以直接筛是不太现实的,我们考虑一下其他做法
统计最小质因子大于等于P的数的个数,我们还可以用容斥原理,但是质数一多就不可做了
这貌似就不可做了= =?

然后就发现me实力犯蠢了一波
我们完全可以用容斥来做 P P 比较小的情况。二分答案,然后容斥出不超过 midP 的数里面,有多少个的最小质因子不小于 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() ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值