Dyd's Blog

He who has a strong enough why can bear almost any how.

luoguP2723 [USACO3.1]丑数 Humble Numbers

时间复杂度的计算是个技术活

丑数

第一次想到的是用一个小根堆,每次取出最小值,乘其它数的积插入堆中,最后取出的第 $n$ 个就是答案,然鹅,时间( $O(n^2\log n)$ )和空间( $O(n^2)$ )上都不允许( $n \le 10^5$ 太艹了)

然后发现其实真正有用的跟新只有 $f[i] * a[i]$ 的形式,其中 $f[i]$ 表示第 $i$ 个丑数, $a[i]$ 表示质数,由于 $k$ 很小,用平衡树维护貌似可做?(黄题啊,平衡树个寂寞啊

最后实在 不想打平衡树 想不出来,一看题解:艹,暴力求第 $n$ 个就好了,加个记录的优化,时间复杂度 $O(nk \xi)$ ,其中 $\xi$ 代表玄学因子(因为没人知道那层while会跑多少),空间复杂度 $O(n)$

无语,这个故事告诉我们不要一看见 $10^5$ 就想 $n\log n$

似乎要long long

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 1e5 + 5, K = 100 + 5;
const LL INF = (LL)1e18 + 7;
int k, n;
LL f[N];
int a[N], b[N];
int main()
{
scanf("%d%d", &k, &n);
for (int i = 1; i <= k; ++i)
scanf("%d", &a[i]);
sort(a + 1, a + 1 + k);
f[0] = 1; //有只有一个质因数的情况,所以从1开始
LL mn;
for (int i = 1; i <= n; ++i)
{
mn = INF;
for (int j = 1; j <= k; ++j)
{
/*
这里是一个优化(不加会TLE),用b记录每个质数对应的最小的f[i]
下一次的i只会更大而不会更小
当然,也可以写二分查找f[i],时间复杂度更稳定一点
*/
while (f[b[j]] * a[j] <= f[i - 1])
++b[j];
mn = min(f[b[j]] * a[j], mn);
}
f[i] = mn;
}
printf("%lld", f[n]);
return 0;
}