4524: [Cqoi2016]伪光滑数

11 篇文章 0 订阅
3 篇文章 0 订阅

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<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<bitset>
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 <Node,vector<Node>,less<Node> > 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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用所述,交错序列是一个仅由0和1构成的序列,其中没有相邻的1(可以有相邻的0)。特征值定义为x^ay^b,其中x和y分别表示0和1出现的次。长度为n的交错序列可能有多个。问题要求计算所有长度为n的交错序列特征值的和除以m的余。 根据引用所述,输入文件包含一个行,该行包含三个整n、a、b和m。其中,1≤n≤10000000,0≤a、b≤45,m<100000000。 为了解决这个问题,可以使用动态规划和矩阵快速幂优化的方法,具体实现可以参考引用提到的相关算法。算法的思路是通过计算长度为n的交错序列的特征值,然后将所有特征值求和并对m取余。 具体步骤如下: 1. 使用动态规划计算长度为n的所有交错序列的特征值,将结果保存在一个矩阵中。 2. 使用矩阵快速幂优化,将动态规划的过程进行优化。 3. 对优化后的结果进行求和,并对m取余。 4. 输出结果。 参考引用给出的博客中的代码实现,可以帮助你更好地理解和实现该算法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [BZOJ5298 CQOI2018 交错序列 【DP+矩阵快速幂优化】*](https://blog.csdn.net/weixin_30892987/article/details/99470493)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值