NKOJ-Unknow 2357數

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的個數 乘積
0 0 0 1 7
0 0 1 1 35
0 1 1 1 105
1 1 1 1 210

(省略了若干步)
這個過程大致來講就是
枚舉
求積
判斷
是否大於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);
}
發佈了79 篇原創文章 · 獲贊 15 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章