#P2793. 第2题-漂亮数
-
1000ms
Tried: 88
Accepted: 17
Difficulty: 6
所属公司 :
美团
时间 :2025年4月5日-开发岗
-
算法标签>数学
第2题-漂亮数
题解
题面描述
给定一个正整数 n,要求统计所有不超过 n 的 “漂亮数” 的个数。
这里“漂亮数”的定义为:
- x 为正整数;
- 存在一个质数 p 满足 xmodp=0 且 p×p≥x。
例如,当 n=10 时,美丽数为 2,3,4,5,6,7,9,10,共 8 个。
思路
我们可以利用质数的性质来枚举“漂亮数”。分析如下:
-
由题意,若 x 为漂亮数,则存在质数 p 和正整数 k,使得
x=p×k且有
k≤p因为 p×p≥x=p×k,可得 p≥k。
-
对于每个质数 p,考虑所有满足
1≤k≤min(p,⌊pn⌋)的 k,则 x=p×k 均为漂亮数。
-
直接枚举所有质数 p(满足 p≤n),然后对于每个 p,枚举 k 的取值,将所有 x=p×k 标记。注意同一个 x 可能由不同的 p 得到,但只需计数一次。
代码分析
- 预处理质数:使用埃氏筛法求出不超过 n 的所有质数。
- 枚举漂亮数:对于每个质数 p,枚举 k=1 到 min(p,⌊pn⌋),将 x=p×k 标记为漂亮数。
- 去重计数:利用布尔数组保证每个漂亮数只计数一次。
时间复杂度主要在于对每个质数 p 的循环,枚举 k 的个数最多为 p(当 p 较小时),总体复杂度足够处理 n≤5×106。
C++
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
// 埃氏筛法求出不超过 n 的所有质数
vector<int> getPrimes(int n) {
vector<bool> isPrime(n + 1, true);
vector<int> primes;
isPrime[0] = isPrime[1] = false;
for (int i = 2; i <= n; i++) {
if (isPrime[i]) {
primes.push_back(i);
if ((long long)i * i <= n) { // 避免乘法溢出
for (int j = i * i; j <= n; j += i) {
isPrime[j] = false;
}
}
}
}
return primes;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
// 获取质数列表
vector<int> primes = getPrimes(n);
// 使用布尔数组标记漂亮数
vector<bool> isBeautiful(n + 1, false);
// 对每个质数 p 枚举合法的 k
for (int p : primes) {
// 如果 p > n,则后续不可能有合法的 x
if(p > n) break;
// k 的上界为 min(p, n / p)
int upper = min(p, n / p);
for (int k = 1; k <= upper; k++) {
int x = p * k;
if (x <= n) {
isBeautiful[x] = true;
}
}
}
// 统计漂亮数个数
int count = 0;
for (int i = 1; i <= n; i++) {
if(isBeautiful[i]) count++;
}
cout << count << "\n";
return 0;
}
Python
# 使用埃氏筛法求出不超过 n 的所有质数
def get_primes(n):
is_prime = [True] * (n + 1)
primes = []
is_prime[0] = is_prime[1] = False
for i in range(2, n + 1):
if is_prime[i]:
primes.append(i)
if i * i <= n:
for j in range(i * i, n + 1, i):
is_prime[j] = False
return primes
def main():
import sys
input_data = sys.stdin.read().strip()
if not input_data:
return
n = int(input_data)
# 获取所有质数
primes = get_primes(n)
# 使用列表标记漂亮数
is_beautiful = [False] * (n + 1)
# 对每个质数 p 枚举合法的 k
for p in primes:
if p > n:
break
# k 的上界为 min(p, n // p)
upper = min(p, n // p)
for k in range(1, upper + 1):
x = p * k
if x <= n:
is_beautiful[x] = True
# 统计漂亮数的个数
count = sum(is_beautiful[1:]) # 从 1 开始计数
print(count)
if __name__ == '__main__':
main()
Java
import java.io.*;
import java.util.*;
public class Main {
// 埃氏筛法求出不超过 n 的所有质数
public static List<Integer> getPrimes(int n) {
boolean[] isPrime = new boolean[n + 1];
Arrays.fill(isPrime, true);
List<Integer> primes = new ArrayList<>();
if(n >= 0) isPrime[0] = false;
if(n >= 1) isPrime[1] = false;
for (int i = 2; i <= n; i++) {
if (isPrime[i]) {
primes.add(i);
if ((long)i * i <= n) {
for (int j = i * i; j <= n; j += i) {
isPrime[j] = false;
}
}
}
}
return primes;
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine().trim());
// 获取质数列表
List<Integer> primes = getPrimes(n);
// 使用布尔数组标记漂亮数
boolean[] isBeautiful = new boolean[n + 1];
// 对每个质数 p 枚举合法的 k
for (int p : primes) {
if(p > n) break;
// k 的上界为 min(p, n / p)
int upper = Math.min(p, n / p);
for (int k = 1; k <= upper; k++) {
int x = p * k;
if(x <= n) {
isBeautiful[x] = true;
}
}
}
// 统计漂亮数个数
int count = 0;
for (int i = 1; i <= n; i++) {
if(isBeautiful[i]) count++;
}
System.out.println(count);
}
}
题目内容
我们定义一个漂亮数是这样的数:
1、该数为正整数
2、设该数为 x ,存在一个质数 p 使得 x mod p=0 且 p∗p>=x
给你一个正整数 n ,你能否求出有多少漂亮数小于等于 n ?
输入描述
输入一行一个正整数 n(1≤n≤5×106)。
输出描述
输出一行一个正整数,代表小于等于 n 的漂亮数的个数。
样例1
输入
10
输出
8