英文原题
中文题译
大意:给定若干素数p1,..pk,找第N个素因子全为这些已知素数的合数。
又是一开始定错了方向,开始了整晚的折磨。唉。最后,完整的过了。
一上来,分析算法时就确定用优先队列找已有的最小的数,把接下来的乘积放如队列中。接着噩梦开始了。马上发现,数可能重复,而优先队列中无法排除重复数,于是想自己写一个可以清除重复数的优先队列,结果发现写不出来。倒是写了个二项堆模板。
无奈之下,用set来记录已经插入的数。依然错,发现输出的是负数,显然是整数越界。弄了很久才想起来long long next=cur*primes[i];之后判断next是否越界是无效的,必须先转换类型(晕,我是猪)。
排除此问题之后,在最后一个测试实例(最大的一个)上发现空间溢出,看了看,用了14M空间。于是,在扩展了N个数之后记录最大值,超过最大值的肯定不是要找的数。这样下来,空间变成8M,不越界了。
但是,时间是1.67秒,超过了1秒的限制。换用自己的Heap,时间差不多。很无奈之下下去吹了吹冷风,想:难道就这样挂了?一定要换方法吗?
最后逐行想过去,vis.find(next)==vis.end() && next<limit,就是它了, 交换了比较顺序,变成next<limit && vis.find(next)==vis.end(),时间立马降低一半,变成0.84秒,通过了。这也算是个常识性错误,唉。改晕头了才会写出这样的代码。也可见set的实现是很烂的,集合越大,性能越差。
/*
ID: blackco3
TASK: humble
LANG: C++
*/
#include <iostream>
#include <queue>
#include <set>
using namespace std;
#define _max_ 100
template < typename t_heap_value, int _max_heap_size_ >
class Heap {
t_heap_value heap[_max_heap_size_+1];
int size ;
private :
inline void swap( int i, int j ){
t_heap_value tmp ;
tmp=heap[i], heap[i]=heap[j], heap[j]= tmp ;
}
inline void del( int pos){
heap[pos]=heap[size--];
while( (pos<<1)<=size ) {
int left=pos<<1, right=left+1 ;
if( right>size || heap[left]<heap[right] )
if( heap[left]<heap[pos] )
swap( pos, left ), pos=left ;
else
break ;
else
if( heap[right]<heap[pos] )
swap( pos, right ), pos=right ;
else
break ;
}
}
public :
Heap(){ size=0; } ;
t_heap_value top(){ return heap[1]; } ;
int empty(){ return size>0; } ;
void pop(){
if(size)
del(1);
};
void push(t_heap_value v ){
heap[++size] = v ;
for( int pos=size; pos>0 ; pos >>= 1 ) {
if( heap[pos>>1]>heap[pos] )
swap(pos, pos>>1);
else
break ;
}
}
void show() {
for( int i=1; i<=size; i++ )
cout << heap[i] << " ";
cout << endl ;
}
};
Heap<int,1000000> heap;
set<int> vis;
int main() {
freopen("humble.in", "r", stdin);
freopen("humble.out", "w", stdout);
int n_prime, n_th, primes[_max_] ;
cin >> n_prime >> n_th ;
for( int i=0; i<n_prime; i++ ){
cin >> primes[i] ;
}
heap.push(1);
for( int i=0, limit=0, count=0; i<n_th; i++ ){
register int cur=heap.top();
heap.pop();
for( int i=0; i<n_prime; i++ ){
long long next=((long long)cur)*primes[i];
if( next > 0x7fffffff )
continue ;
if( count<n_th ){
if( vis.find(next)==vis.end() ){
vis.insert(next), heap.push(next);
count++, limit=limit>next? limit : next ;
}
} else {
if( next<limit && vis.find(next)==vis.end() )
vis.insert(next), heap.push(next);
}
}
}
cout << heap.top() << endl ;
return 0;
}
补充,重写了一个暴力枚举的,时间上比之前的好。用一个数组记录每个素数上一次乘的数的位置和值,这样,每个乘法只做一次。用这种方式还可以记录出指数和(参见邮票Stamps一题)。
/*
ID: blackco3
TASK: humble
LANG: C++
*/
#include <iostream>
using namespace std;
#define _max_prime_ 100
#define _max_val_ 100001
int main() {
freopen("humble.in", "r", stdin);
freopen("humble.out", "w", stdout);
int n_prime, n_th, primes[_max_prime_], pos[_max_prime_], pval[_max_prime_] ;
cin >> n_prime >> n_th ;
for( int i=0; i<n_prime; i++ ){
cin >> primes[i] ;
pos[i]=0, pval[i]=primes[i] ;
}
int val[_max_val_]={1} ;
for( int ival=1; ival<=n_th; ival++ ){
register int min_val=0x7fffffff, min_pos ;
do{
min_val=0x7fffffff ;
for( int x=0; x<n_prime; x++ )
if( pval[x] < min_val )
min_val=pval[x], min_pos=x ;
if( min_val > val[ival-1] )
break ;
pval[min_pos] = val[++pos[min_pos]]*primes[min_pos] ;
}while( true ) ;
val[ival]=min_val ;
}
cout << val[n_th] << endl ;
return 0;
}