题意简化:
已知有 n 个质数 a_i,求第 k 个包含了这些质因子的数。
算法:
这个蒟蒻看了各位大佬的 O(nk) 算法,表示不懂,于是现在这个使用了优先队列,也就是 O(k \times n \times \log(n \times k)),所以开个氧气也能过。
思路:
笔者第一次做,发现题目中明确指出了 ans \leq 2 \times 10^9,所以想起来了要开 long long
,刚好学习了优先队列。而优先队列有一个特性——可以排序,然后……写出了这个代码。
ll number[20005];
int bfs(int a[]){
int cnt=n,id=0,nbs=n;
for(int i=1;i<=n;i++)q.push(a[i]);
while(!q.empty()){
id++;
int top=q.top();
q.pop();
if(id==k)return top;
if(cnt>k)continue;
for(int i=1;i<=n;i++){
bool flag=1;int t=a[i]*top;
for(int i=1;i<=nbs;i++){
if(number[i]==t){flag=0;break;}
}
if(flag)q.push(t);
}
}
}
很恭喜,成功地只 AC 了一个点,还 TLE 了一个点。
现在我们可以发现,判重实际上很浪费时间,所以现在我们记录上一次的数字(因为一个大的数字不可能凑出来一个小的数字),节约时间。
再一次观察发现,函数内的 int
和优先队列使用的 long long
不为同类型,所以需要将 int
修改为 long long
。
于是有了现在这个代码。
int bfs(int a[]){
int id=0;
ll last=-1,top,t;
for(int i=1;i<=n;i++)q.push(a[i]);
while(!q.empty()){
top=q.top();
q.pop();
if(top==last)continue;
id++;
if(id==k)return top;
last=top;
for(int i=1;i<=n;i++){
t=a[i]*top;
if(t>P)continue;
q.push(t);
}
}
}
很恭喜,还是 TLE 了一个点。
到了这里,笔者想起来开 O2 优化,开了优化后,是 MLE,爆空间了。
所以……你还记得我们说的那个 ans \le 2 \times 10^9 吗?
既然 ans \leq 2 \times 10^9(int
的最大值近似为 2^{31}-1 约为 2.147 \times 10^9),那么答案就在 int
类型的区间里。
所以,我们把改成 int
类型的队列。
很恭喜,开 O2 优化,过了。
停一下,我们整理一下思路:
1. 结果需要开 long long
,因为两个大于等于 2^{15.5} 的数字相乘,是会超过 int
的最大值的,但是队列只用开 int
,因为答案不超过 int
的最大范围。
2. 不需要记录结果数字有没有重复(因为两个大于一的数字相乘一定越乘越大),但是需要记录这个数字有没有用过。
3. 需要 O2 优化。可能是 STL 的常数比较大。
ACcode:
//头文件略
#define ll long long
int n,k;
priority_queue< int , vector<int> , greater<int> > q;
const int P=2000000000;//方便之后书写
ll bfs(int a[]){
int id=0;
sort(a+1,a+n+1);//这里可以方便后面的if(t>P)可以直接break,方便我们节约时间复杂度。
for(int i=1;i<=n;i++)q.push(a[i]);//入队
ll last=-1,top,t;//last代表上一次的数字
while(!q.empty()){
top=q.top();
q.pop();
if(top==last)continue;//跳过循环
id++;last=top;//id统计有多少个有效数字了
if(id==k)return top;//当前是正确的数字,那么返回队列的顶端
for(int i=1;i<=n;i++){
t=a[i]*top;
if(t>P)break;//特判
q.push(t);//入队
}
}
}
int main(){
//读入略
printf("%d",bfs(input));
return 0;
}
附:提交记录
Comments NOTHING