[bzoj4524] [loj#2047] [Cqoi2016] 伪光滑数

Description

若一个大于 (1) 的整数 (M) 的质因数分解有 (k) 项,其最大的质因子为 (Ak) ,并且满足 (Ak^K leq N) , (Ak<128) ,我们就称整数 (M)(N-) 伪光滑数。现在给出 (N) ,求所有整数中,第 (K) 大的 (N-) 伪光滑数。

Input

只有一行,为用空格隔开的整数 (N)(K)
(2 leq N leq 10^18)(1 leq K leq 800000),保证至少有 (K) 个满足要求的数

Output

只有一行,为一个整数,表示答案。

Sample Input

12345 20

Sample Output

9167


想法

目前见到的求 第 (K) 大/小的题大概有三种做法:
1.二分判断。
2.在 (K) 不太大时,可以从大到小/从小到大枚举,用数据结构维护当前最大,取出最大值后用次大值(扩展值)代替它。
3.堆的 (K) 路归并(我也不懂这是啥 %%%标算

这道题中我用的是第二种做法。
首先一个性质,对于数 (i)(i leq Ak^k leq N) ,假设 (Ak^k)(i) 的“特征数”
由于 (Ak leq 128) , 而128以内的质数仅31个,所以 (N) 以内的特征数值很少
所以大体想法就是优先队列维护所有的“特征数”对应的 (i) 的最大值,每次取出最大,用次大替代就行了。

初始状态时每个特征数对应的最大值很好搞,就是 (Ak^k) (特征数本身的值)
但次大是多少呢? (Good) (Question!)
我发现次大有两种情况,一种把一个 (Ak) 换为 (A_{k-1}),一种是将某一个 (Ax) 换为 (A_{x-1})
有点乱。

试着搞出一种扩展顺序,即“分层扩展”。
初始状态,某个特征数 (Ak^k) 对应的最大值是 (k)(Ak) 相乘,不妨称它的层数为0
在扩展 (Ak^k) 时,取出一个 (Ak) ,换成 (A1,A2,...,A_{k-1}) ,即 (Ak^{k-1} imes Ax) ,称它们的层数为1(替换了一个 (Ak)
在扩展 (AK^{k-1} imes Ax) 时,再取出一个 (Ak) ,换成 (A1,A2,...,Ax) ,即 (Ak^{k-2} imes Ax imes Ay) ,称它们的层数为2(替换了两个 (Ak)
以此类推……
注意到每次扩展时,(Ay leq Ax) ,这是为了防止同一个数,由于被换的顺序不同而被计算多次。

这样可以保证每次扩展后,该特征数的次大值都在优先队列中嘛?(注意,是“在优先队列中”,但不一定是这次扩展加进去的)
首先,显然每个数扩展出的数都比它本身小,所以对于所有可以通过这种方法扩展出、但没加到优先队列中的数,一定说明扩展出它的数没加到优先队列中或在队列中还没成为最大值,即这些数不是我要的“次大值”
而是不是所有数都可以通过这种方法扩展出呢?显然可以!

其实这就是模拟搜索吧。。。复杂度 (O(128K)) 可以卡过。


一些启示

(Orz)
分层扩展……?
要有一些顺序的思想吧,不必每次只扩展一个……?
【我也不知道哭唧唧】


代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cmath>
#include<vector>

using namespace std;

typedef long long ll;

ll n;
int k;
int p[31]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127};

struct data{
	ll t;
	int x,y,z; // p[x]^y,nxtmin--p[z]
	data() { t=0; x=y=z=0; }
	data(ll a,int b,int c,int d) { t=a; x=b; y=c; z=d; }
	bool operator < (const data &b) const{ return t<b.t; }
};
priority_queue<data> q;

int main()
{
	scanf("%lld%d",&n,&k);
	
	ll x;
	for(int i=0;i<31;i++){
		x=1;
		for(int j=1;1ll*x*p[i]<=n;j++){
			x*=p[i];
			q.push(data(x,i,j,i-1));
		}
	}
	
	data tmp;
	while(k--){
		tmp=q.top(); q.pop();
		if(tmp.y>1){
			for(int i=tmp.z;i>=0;i--)
				q.push(data(tmp.t/p[tmp.x]*p[i],tmp.x,tmp.y-1,i));
		}
	}
	printf("%lld
",tmp.t);
	
	return 0;
} 
既然选择了远方,便只顾风雨兼程
原文地址:https://www.cnblogs.com/lindalee/p/11488876.html