[数学基础] 11 丑数筛
丑数筛
给定一个质数集合\(S=\{p_1,p_2,..,p_k\}\),只由这些质数相乘得到的数我们成为丑数(Humble/Ugly Numbers)。习惯上,我们认为第一个丑数是1,但是也可能不是,所以看清题意。记第\(n\)大 的丑数为\(h[n]\)。
无论哪种方法,这个丑数实际上都是可能非常大的,建议直接上__int128!!
第一种筛法,也是最常用的,就是搞一个优先队列,每次提出来优先队列中最小的数,注意去重。
这种方法的时间复杂度是\(O((n\times k)\log (n\times k))\)
#define ll __int128
const ll INF = 9e37, N = 5e4 + 5;
ll h[N], p[3] = {2, 3, 5}, cnt[3];
priority_queue<ll, vector<ll>, greater<ll> > pq;
void Humble(int n){
pq.push(1);
ll last = 1;
for (int i=1;i<=n;++i){
for (int j=0;j<3;++j){
pq.push(last * p[j]);
}
last = pq.top();
while (!pq.empty() && last == pq.top()) pq.pop();
h[i] = last;
}
}
第二种线性筛法,相当于做了一个DP+双指针,复杂度\(O(n\times k)\)。注意,这里的下标是从0开始的,并且,INF要足够足够大!!!!
(嗯?我这么大一个代码怎么没贴上来 赶紧亡羊补牢ing)
ll h[N], p[3] = {2, 3, 5}, cnt[3];
void Humble(int n){
h[0] = 1; // 下标0的地方是第一个数
for (int i=1;i<=n;++i){
h[i] = INF;
for (int j=0;j<3;++j){
while (p[j] * h[cnt[j]] <= h[i - 1]) cnt[j]++;
if (p[j] * h[cnt[j]] < h[i]){
h[i] = p[j] * h[cnt[j]];
}
}
}
}
// cnt[j]记录的是当前乘的最后一个质因子是p[j]的, 最小的那个数的下标
// h[i]一定从这些数中产生