• [CSP-S模拟测试]:数论(数学)


    题目传送门(内部题11)


    输入格式

    第一行,三个整数$T,K,M$,分别代表数据组数、良好标准和整数范围。
    接下来$T$行,每行一个整数$n_i$,代表一个询问。


    输出格式

    输出$T$行,在第$i$行对于询问$i$输出一个整数,代表第$n_i$个良好的整数。
    保证答案一定不超过给定的$M$。


    样例

    样例输入1:

    1 0 23333
    10

    样例输出1:

    20

    样例输入2:

    3 5 998244353
    28
    165
    233

    样例输出2:

    42
    9360
    63360


    数据范围与提示

    样例1解释:

    前$10$个优秀的整数是$1,2,3,4,6,8,10,12,18,20$。

    数据范围:

    对于所有数据,$1leqslant Tleqslant 20,0leqslant Kleqslant 233,1leqslant n_ileqslant Mleqslant {10}^{18}。


    题解

    对于一个质数$p$,我们考虑所有仅包含小于$p$的质因子的正整数集$G$。不难发现:
      若$xin G$,且在$G$中已经有超过$K$个小于$x$的整数约数个数多于$x$,即$x$一定不是良好的,则$xp^c(cgeqslant 0)$也一定不可能是良好的。
    这样我们就可以得到一个初步的想法。开始我们认为仅有$1$是良好的,枚举质因子$p$,对于每一个原来认为是良好的数$x$,将$xp^c(cgeqslant 0)$加入候选列表,接着将候选列表排序,除去已经可以确定不是良好的数,进入下一轮迭代。容易证明,在这个算法中,筛去一个不是良好的数$x$,是不会在后续过程中令一个原本不是良好的数,变成一个良好的数的,故筛去良好的数的过程是合法的剪枝。
    然而枚举的质因子的范围有多大呢?联想$K=0$这一经典问题,我们知道对于${10}^{18}$的范围,考虑前$20$个质因子都绰绰有余了,因为将更大的质因子加入是非常不优的。在$K$更大的时候,我们采用“迭代至稳定”的思想,每一轮迭代后检查答案是否变化,如果在较长一段迭代后答案无任何变化,我们就认为质因子$p$的上界已经达到。经过实践,在$K=233$时,$p$的最大值取到$293$即可。
    我们考虑如何在一轮迭代中除去确定不是良好的数。考虑维护前$K+1$大值,从小到大枚举候选列表中的数$x$,若$x$小于第$K+1$大值,我们就把这个数除去。否则更新前$K+1$大值。根据上述描述可以大致估算复杂度。设$K=233$时,${10}^{18}$内良好的数的数量为$N$,经过实践,可以知道$N$约为$50,000$。每次扩展最多把一个数扩展成$log M$个数,在剪枝完毕后,列表大小又回归到$N$以下。

    时间复杂度:$Theta((N imes K imes max(p)log M)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    int T,K;
    long long M;
    int prime[]={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,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293};
    int cnt,num,size;
    pair<int,long long> heap[200000],que[200000],flag[200000];
    bool cmp(pair<int,long long> x,pair<int,long long> y){return x.second==y.second?x.first<y.first:x.second<y.second;}
    void up(int x)
    {
    	while(x>1)
    		if(heap[x]<heap[x>>1])
    		{
    			swap(heap[x],heap[x>>1]);
    			x>>=1;
    		}
    		else break;
    }
    void insert(pair<int,long long> x){heap[++size]=x;up(size);}
    void down(int x)
    {
    	int s=x<<1;
    	while(s<=size)
    	{
    		if(s<size&&heap[s]>heap[s|1])s|=1;
    		if(heap[s]<heap[x])
    		{
    			swap(heap[s],heap[x]);
    			x=s;
    			s=x<<1;
    		}
    		else break;
    	}
    }
    void change(pair<int,long long> x){heap[1]=x;down(1);}
    int main()
    {
    	scanf("%d%d%lld",&T,&K,&M);
    	que[++cnt]=make_pair(1,1);
    	for(int i=0;i<62;i++)
    	{
    		num=0;
    		long long lft=0,rht=M/prime[i],k=0;
    		while(lft<=rht)
    		{
    			lft=max(lft*prime[i],1LL);
    			k++;
    			for(int j=1;j<=cnt&&lft*que[j].second<=M;j++)
    				flag[++num]=make_pair(que[j].first*k,lft*que[j].second);
    		}
    		sort(flag+1,flag+num+1,cmp);
    		int lst=cnt;
    		cnt=size=0;
    		for(int j=1;j<=min(K+1,num);j++)
    		{
    			insert(flag[j]);
    			que[++cnt]=flag[j];
    		}
    		for(int j=min(K+1,num)+1;j<=num;j++)
    			if(flag[j].first>=heap[1].first)
    			{
    				change(flag[j]);
    				que[++cnt]=flag[j];
    			}
    		if(lst==cnt)break;
    	}
    	while(T--)
    	{
    		int x;
    		scanf("%d",&x);
    		printf("%lld
    ",que[x].second);
    	}
    	return 0;
    }
    

    rp++

  • 相关阅读:
    HTTP Basic 验证客户端 C#实现笔记
    泗洪高薪行业
    C#中Math的使用总结
    Android音频底层调试-基于tinyalsa
    我看项目管理第一回:认识利益相关方,提高思想意识
    【剑指Offer学习】【面试题19 :二叉树的镜像】
    算法
    zTree实现地市县三级级联DAO接口实现
    Unix/Linux环境C编程新手教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建
    正尝试在 OS 载入程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内执行托管代码,这样做会导致应用程序挂起。
  • 原文地址:https://www.cnblogs.com/wzc521/p/11367276.html
Copyright © 2020-2023  润新知