C. 丑数 B C D 传统题 1000ms 256MiB 给你四个正整数:n、a、b、c,请你设计一个算法来找出第n个“丑数”。 所谓丑数,在这里是指可以被a或b或c整除的正整数。 输入格式 一行4个正整数n、a、b、c; 输出格式 输出一个整数,表示答案。 样例数据 输入1 3 2 3 5 输出1 4 输入2 4 2 3 4 输出2 6 输入3 1000000000 2 217983653 336916467 输出3 1999999984 样例1解释 丑数序列为2,3,4,5,6,8,9,10...其中第3个是4。 数据范围 对于30%的数据,1≤n,a,b,c≤100 对于60%的数据,1≤n,a,b,c≤10000 对于100%的数据,1≤n,a,b,c≤1000000000 c++ 数组名字叫a不要注释
时间: 2025-03-29 07:08:49 浏览: 60
<<
要找到第 n 个“丑数”,我们可以使用二分查找结合容斥原理的方法高效地解决问题。
### 思路分析:
我们定义一个函数 `count(x)` 来计算小于等于 x 的所有能被 a 或 b 或 c 整除的数字的数量。然后通过二分法不断缩小搜索区间,直到找到满足条件的答案为止。
**步骤:**
1. **初始化变量:** 设定初始值 low = 0 和 high = INT_MAX(因为可能的最大范围为 $1e9$)。
2. **构造 count 函数:** 使用容斥原理来确定有多少个小于等于 mid 的数可以被 a、b、c 中的一个整除:
- 可以被单一数字整除的部分直接加上;
- 对于两两组合部分减去重复计数的情况;
- 加回三个同时能够整除的结果防止多减。
3. **调整边界:** 根据当前 mid 是否大于目标数量动态调整 low 或 high。
4. **返回结果:** 当退出循环时,low 即为我们寻找的目标答案。
以下是完整代码:
```cpp
#include <bits/stdc++.h>
using namespace std;
long long gcd(long long a, long long b) {
return !b ? a : gcd(b, a % b);
}
// 容斥原理求解 [1,x] 区间内可被 a/b/c 分别整除的数目之和
long long LCM[8]; // 最大只需存储 C(3,3)=1 种情况即可扩展到更多因子支持.
int a[4];
long long lcm(int i,int j){
return (long long)a[i]*a[j]/gcd(a[i],a[j]);
}
void precompute_lcms(){
for(int i=0;i<3;i++)for(int j=i+1;j<3;j++)LCM[(1<<i)|(1<<j)]=(lcm(i,j));
for(int mask=1;mask<(1<<3);mask++){
int bits=__builtin_popcount(mask);
if(bits==3){
LCM[mask]=((long long)a[0]*a[1])/gcd(a[0],a[1]);
LCM[mask]=(LCM[mask]*(long long )a[2])/gcd(LCM[mask],a[2]);
}
}
}
long long count(long long m){
long long res=0;
for(int mask=1;mask<(1<<3);mask++){
if(__builtin_popcount(mask)&1)
res+=m/LCM[mask];
else
res-=m/LCM[mask];
}
return res;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
long long n;
cin>>n>>a[0]>>a[1]>>a[2];
sort(a,a+3);
do{
bool flag=true;
for(int i=0;i<2 &&flag ;i++)
if(a[i]%a[i+1]==0 || a[i+1]%a[i]==0 )
continue;
else {flag=false;}
}while(next_permutation(a,a+3));
precompute_lcms();
long long left=0,right=2*(long long)(1e9),mid,res=-1;
while(left<=right){
mid=(left + right)/2;
if(count(mid)>=n ){
res=mid;
right=mid-1;
}
else{
left=mid+1;
}
}
cout <<res;
}
```
这段程序首先利用辗转相除法获取最大公约数,并用它来构建最小公倍数值表,接着应用二分加容斥原理快速定位符合条件的数字位置并输出正确结果.
#### 解释:
上述代码实现了如下的逻辑过程:
1. 计算任意两个数以及全部三者的最小公倍数存入数组中备用。
2. 利用排列组合去除冗余关系使得后续更简洁明了处理。
3. 基本上基于数学理论进行优化加速从而避免暴力枚举带来的效率低下问题。
### 注意事项:
确保数据类型足够容纳可能出现的大整型运算结果;另外注意当某些特定输入下可能导致错误排序修正机制的存在与否影响最终效果表现差异性。
阅读全文