ABC 250 | D - 250-like Number

题目描述

给定一个数N,找出[1,N]范围内,可以表示成p×q3的形式的数的个数,其中p<qpq均为质数。

数据范围

1N1018

题目解析

首先观察数据范围发现不可以枚举i,i[1,N],然后判断该数是否满足题目描述性质。考虑逆向思维,枚举p,q并构造满足题目要求的数,然后判断是否在1N范围内。
N=p×q3N1018q106
所以只需预处理出106范围内的质数,可以用线性筛法。

预处理得到106范围内的质数后(不到105个),可以通过两种方法计算在1N范围内的p×q3的个数。(需注意,由于pq均为质数,所以只要pq不完全相同,组合出的数就不同,可以从质因数分解的角度证明)。

方法一:二分查找O(NlogN)

枚举所有的p,查找满足条件的q
假设pprimes[i],起初为满足q>p设置边界条件为l=i+1,r=cnt,这样做无法通过样例。后调整为l=0,r=cnt,然后判断是否有l>i,可以AC。

方法二:双指针算法O(N)

容易发现pq移动的单调性,因此可用双指针算法,每次答案加上两个指针之间的距离。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;

const int N = 1e6 + 10;

int primes[N], cnt;
bool st[N];
ll n;

void get_primes(int n)
{
    for (int i = 2; i <= n; i ++ )
    {
        if (!st[i]) primes[cnt ++ ] = i;
        for (int j = 0; primes[j] <= n / i; j ++ )
        {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}

ll check(ll p, ll q){
    double x = (double)p * (double)q * (double)q * (double)q;
    if(x > 4e18) return 4e18;
    return p * q * q * q;
}

int main()
{
    get_primes(1e6);
    scanf("%lld", &n);

    ll ans = 0;
    for(int i = 0; i < cnt; i ++){
        ll p = primes[i];
        int l = 0, r = cnt;
        while(l < r){
            int mid = l + r + 1 >> 1;
            ll q = primes[mid];
            if(check(p, q) <= n) l = mid;
            else r = mid - 1;
        }
        if(l > i) ans += (l - i);
    }
    printf("%lld\n", ans);

    return 0;
}

模板积累

//学会处理一个爆ll的数
ll check(ll p, ll q){
    double x = (double)p * (double)q * (double)q * (double)q;
    if(x > 4e18) return 4e18;
    return p * q * q * q;
}
posted @   小菜珠的成长之路  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示