NKOJ-Unknow 2357数

22 篇文章 0 订阅
12 篇文章 0 订阅

2357 数
题目描述

一个数字被称之为 2357 数,当且仅当其所有大于 1 的因子均能被 2/3/5/7 中的某一个整除。对于数字 N,你需要求出不小于 N 的最小 2357 数。

输入格式

一个数字 N。

输出格式

一个数字表示最小的 2357 数。

样例输入

209

样例输出

210

数据范围和注释

对于 30%的数据,N≤5000。
对于 60%的数据,N≤10^9。
对于 100%的数据,N≤10^13。

每次遇到这种没有情景代入的都无话可吐槽 就很尴尬

题解

读题

这个数字的组成十分简单,分解出来的质因数只有2 3 5 7这四个
但是这个数字的要求有点过分,它要求不比N小的最小2357数

解法一 快速幂强行枚举

根据简单的计算你会发现2^44>10^13

也就是说对于一个满足百分之百数据范围的2357数,它分解出来的质因数的个数最多也只有43个
那么我们依次枚举2 3 5 7的个数,直接计算这么多个数字乘出来的积,满足要求就对结果进行更新

例如
N=209

我们进行枚举
2的个数3的个数5的个数7的个数乘积
00017
001135
0111105
1111210

(省略了若干步)
这个过程大致来讲就是
枚举
求积
判断
是否大于N –若是–> 与当前结果进行比较 —–>取更小的那一个作为结果

优化
首先我们可以求出最多个质因数的个数k

对于N,我们能够求出的比它大的质因数最多的最小2357数就是2的k次方
大概意思就是
    对于2^k>=N,k最小的时候,就是我们要求的2357数最多个质因数的个数

举个例子
    对于N=125,因为2^7=128>=125,也就是说七个2相乘就可以获得一个满足不小于N的2357数
    所以组成满足要求的2、3、5、7的个数加起来不超过7个

这样我们就只需要在1~k的范围内枚举即可
(此处是因为2^k可以通过位运算快速得到,所以推荐进行优化)
(另外还可以通过7^k>=N求出最少个质因数的个数,自行抉择)

第二个,重头戏 快速幂优化

我觉得这个没什么好说的
说出来大家都懂,就是不需要取模
快速地进行求解
这个解法不会超时的核心所在

大概算一下,这道题目的时间复杂度最大为枚举的次数O(k^4)
快速幂的复杂度可以计入系数,平均下来是log k

解法二 单调队列

先举个例子

对于N=34

我们先在队列中加入1
然后取出1并进行*2 *3 *5 *7的操作
加入一个单调递增的队列

由于队列的单调性,我们从队列中取出的一定是当前最小的2357数

取2 -> 加入4 6 10 14
当前队列 3 4 5 6 7 10 14

由于之后的操作都是在大于等于当前这个数的基础上再乘一个数字得到的,所以一定是大于当前数字的
所以我们现在的到的数字一定是在现在以及未来(除去我们已经判断过不要的2357数)的2357数中最小的

通过这样的操作,当我们取出第一个大于等于N的2357数时,那就绝对是满足题目条件的数字

唯一需要注意的是,像是2*7和7*2这样的操作会莫名其妙多加入很多个相同的数字进去
所以我们需要判重(判断重复)以免多加入很多很多相同的数字导致莫名其妙增加了很多很多时间

悄悄附上一个很弱的操作

#include <queue>

priority_queue<int,vector<int> >go;

这样得到的go就是一个小根堆,go.push(x)把x加入进去,go.front()取当前最小数,go.pop()删除当前最小数

附上对拍代码(没用以上任何操作的弱代码)

#include <iostream>
#include <cstdio>
using namespace std;

long long er[101234],san[101234],wu[101234],qi[101234];
long long cer=0,per=1,csan=0,psan=1,cwu=0,pwu=1,cqi=0,pqi=1;

void push(long long x)
{
    er[++cer]=x*2;
    san[++csan]=x*3;
    wu[++cwu]=x*5;
    qi[++cqi]=x*7;
}

long long GET()
{
    long long tmp,flag;
    if(per<=cer)tmp=er[per],flag=2;
    if(psan<=csan)
    {
        if(san[psan]<tmp)tmp=san[psan],flag=3;
        else while(san[psan]==tmp)psan++;
    }
    if(pwu<=cwu)
    {
        if(wu[pwu]<tmp)tmp=wu[pwu],flag=5;
        else while(wu[pwu]==tmp)pwu++;
    }
    if(pqi<=cqi)
    {
        if(qi[pqi]<tmp)tmp=qi[pqi],flag=7;
        else while(qi[pqi]==tmp)pqi++;
    }
    if(flag==2)per++;if(flag==3)psan++;
    if(flag==5)pwu++;if(flag==7)pqi++;
    return tmp;
}

int main()
{
    freopen("2357.in","r",stdin);
    freopen("2357.out","w",stdout);
    long long n,a=1;
    scanf("%lld",&n);
    while(a<n)
    {
        push(a);
        a=GET();
    }
    printf("%lld",a);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值