4524: [Cqoi2016]伪光滑数

4524: [Cqoi2016]伪光滑数

Time Limit: 10 Sec   Memory Limit: 512 MB
Submit: 280   Solved: 133
[ Submit][ Status][ Discuss]

Description

若一个大于R的整数J的质因数分解有F项,其最大的质因子为ak,并且满足ak^k≤N,
ak<128,我们就称整数J为N-伪光滑数。
现在给出L,求所有整数中,第E大的N-伪光滑数。

Input

只有一行,为用空格隔开的整数L和E。
2 ≤ N ≤ 10^18, 1 ≤ K ≤ 800000,保证至少有 E 个满足要求的数

Output

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

Sample Input

12345 20

Sample Output

9167

HINT


Source

[ Submit][ Status][ Discuss]




正解是可持久化可并堆 + dp,,看了题解才会= =

先筛出2~128内所有质数,分别给它们一个编号

定义f[i][j]:质因数最大为pri[i],由j个质因数组成的数的集合

定义g[i][j]:质因数最大为pri[1] ~ pri[i],由j个质数组成的数的集合

定义U*g为集合U中所有数字乘以g,定义A + B为集合A,B取并集

那么很好写出f,g的方程,f[i][j] = ∑g[i-1][k]*pri[i]^(j-k),g[i][j] = g[i-1][j] + f[i][j]

询问的话先把所有f[i][j]中最大的数都扔进堆里,每次取出最大的数字,把它所在集合的次大数扔进堆

不断重复K次就是答案了

f,g的集合可以用可持久化可并堆 + 懒惰标记解决

这样就能保证存数集最大数的那个堆里的元素大小线性,因为每次取出一个数新放两个数

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

typedef long long LL;
typedef double DB;
const int maxn = 8E5 + 80;
const int T = 15;

struct Heap{
	int L,ch[2]; LL val,Mark;
}h[maxn*T];

struct Node{
	LL val; int num; Node(){}
	Node(int num,LL val): num(num),val(val){}
	bool operator < (const Node &B) const {return val < B.val;}
};

LL N,mi[100][100],pri[100];
int K,tot,cnt,f[100][100],g[100][100];
bool not_pri[200];

priority_queue ,less > Q;

void Pre_Work()
{
	for (int i = 2; i < 128; i++)
	{
		if (!not_pri[i]) pri[++tot] = i;
		for (int j = 1; j <= tot; j++)
		{
			int Nex = i*pri[j];
			if (Nex >= 128) break;
			not_pri[Nex] = 1;
			if (i % pri[j] == 0) break;
		}
	}
	for (int i = 1; i <= tot; i++)
	{
		mi[i][0] = 1;
		for (int j = 1; j <= 64; j++)
		{
			if ((DB)(pri[i]) > (DB)(N) / (DB)(mi[i][j-1])) break;
			mi[i][j] = mi[i][j-1]*pri[i];
		}
	}
}

void pushdown(int x)
{
	if (h[x].Mark == 1) return;
	h[x].val *= h[x].Mark;
	for (int i = 0; i < 2; i++)
		if (h[x].ch[i])
		{
			h[++cnt] = h[h[x].ch[i]];
			h[cnt].Mark *= h[x].Mark; h[x].ch[i] = cnt;
		}
	h[x].Mark = 1;
}

int Merge(int x,int y)
{
	if (!x) return y; if (!y) return x;
	pushdown(x); pushdown(y);
	if (h[x].val < h[y].val) swap(x,y);
	int ret = ++cnt; h[ret] = h[x];
	h[ret].ch[1] = Merge(h[ret].ch[1],y);
	if (h[h[ret].ch[1]].L > h[h[ret].ch[0]].L) 
		swap(h[ret].ch[0],h[ret].ch[1]);
	h[ret].L = h[h[ret].ch[1]].L + 1; return ret;
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	cin >> N >> K; Pre_Work();
	f[0][0] = g[0][0] = ++cnt; h[cnt].Mark = h[cnt].val = 1;
	for (int i = 1; i <= tot; i++)
		for (int j = 0; j <= 64; j++)
		{
			if (!mi[i][j]) {g[i][j] = g[i-1][j]; continue;}
			for (int k = 0; k < j; k++) if (g[i-1][k])
			{
				LL now = h[g[i-1][k]].val*h[g[i-1][k]].Mark;
				h[++cnt] = h[g[i-1][k]]; h[cnt].Mark *= mi[i][j-k];
				f[i][j] = Merge(f[i][j],cnt);
			}
			g[i][j] = Merge(g[i-1][j],f[i][j]);
			if (!f[i][j]) continue;
			pushdown(f[i][j]); Q.push(Node(f[i][j],h[f[i][j]].val));
		}
	for (int I = 1; ; I++)
	{
		Node k = Q.top(); Q.pop(); int now = k.num;
		if (I == K) {cout << k.val; break;}
		for (int i = 0; i < 2; i++)
			if (h[now].ch[i])
			{
				int Nex = h[now].ch[i]; pushdown(Nex);
				Q.push(Node(Nex,h[Nex].val));
			}
	}
	return 0;
}

你可能感兴趣的:(左偏树,堆,dp)