1. 程式人生 > 其它 >【Coel.解題報告】【場外人士的拙劣表演】P7960 [NOIP2021]報數

【Coel.解題報告】【場外人士的拙劣表演】P7960 [NOIP2021]報數

題前碎語

很遺憾,今年沒能參加\(NOIp\),甚至\(CSP\)都沒去。
因為初三一整年都在頹廢,覺得自己太菜了只能當分母
所以就作為一個場外人士來做題了!

題目梗概:[NOIP2021]報數

洛谷傳送門
報數遊戲是一個廣為流傳的休閒小遊戲。參加遊戲的每個人要按一定順序輪流報數,但如果下一個報的數是 \(7\) 的倍數,或十進位制表示中含有數字 \(7\),就必須跳過這個數,否則就輸掉了遊戲。

在一個風和日麗的下午,剛剛結束 SPC20nn 比賽的小 r 和小 z 閒得無聊玩起了這個報數遊戲。但在只有兩個人玩的情況下計算起來還是比較容易的,因此他們玩了很久也沒分出勝負。此時小 z 靈光一閃,決定把這個遊戲加強:任何一個十進位制中含有數字 \(7\)

的數,它的所有倍數都不能報出來!

形式化地,設 \(p(x)\) 表示 \(x\) 的十進位制表示中是否含有數字 \(x\),若含有則 \(p(x) = 1\),否則 \(p(x) = 0\)。則一個正整數 \(x\) 不能被報出,當且僅當存在正整數 \(y\)\(z\) ,使得 \(x = yz\)\(p(y) = 1\)

例如,如果小 r 報出了 \(6\) ,由於\(7\) 不能報,所以小 z 下一個需要報 \(8\);如果小 r 報出了 \(33\),則由於 \(34 = 17 \times 2\)\(35 = 7 \times 5\) 都不能報,小 z 下一個需要報出 \(36\)

;如果小 r 報出了 \(69\),由於 \(70 \sim 79\) 的數都含有 \(7\),小 z 下一個需要報出 \(80\) 才行。

現在小 r 的上一個數報出了 \(x\),小 z 想快速算出他下一個數要報多少,不過他很快就發現這個遊戲可比原版的遊戲難算多了,於是他需要你的幫助。當然,如果小 r 報出的 x 本身是不能報出的,你也要快速反應過來小 r 輸了才行。

由於小 r 和小 z 玩了很長時間遊戲,你也需要回答小 z 的很多個問題。

輸入格式
從number.in中讀入資料。
第一行,一個正整數 \(T\) 表示小 z 詢問的數量。

接下來 \(T\) 行,每行一個正整數 \(x\)

,表示這一次小 r 報出的數。

輸出格式
輸出至number.out中。
輸出共 \(T\) 行,每行一個整數,如果小 r 這一次報出的數是不能報出的,輸出 \(-1\),否則輸出小 z 下一次報出的數是多少。
【資料範圍】

對於 \(10\%\) 的資料,\(T \leq 10\)\(x \leq 100\)
對於 \(30\%\) 的資料,\(T \leq 100\)\(x \leq 1000\)
對於 \(50\%\) 的資料,\(T \leq 1000\)\(x \leq 10000\)
對於 \(70\%\) 的資料,\(T \leq 10000\)\(x \leq 2 \times {10}^5\)
對於 \(100\%\) 的資料,\(1 \le T \leq 2 \times {10}^5\)\(1 \le x \leq {10}^7\)


\(NOIp\)的第一道題,難度十分友好(個人感覺黃題上位)
第一眼看這題就想到用打表,不過仔細思考了一下發現\(10^7\)不算太大,可以直接在程式裡打。其實也是因為我懶得再寫一個打表程式
篩完直接二分查詢就行了,時間複雜度為\(O(maxn +Tlogn)\)(如果在程式外打表就是\(O(Tlogn)\)),可以通過本題。
程式碼如下:

#include<cstdio>
#include<algorithm>
#include<vector>
#define maxn 10001000
using namespace std;
int T,n;
bool vis[maxn];
vector<int>excel;
inline bool check(int i)//篩出帶7 的數
{
    while(i)
    {
        if(i%10==7)return true;
        i/=10;
    }
    return false;
}
inline void Hit_The_Excel()//愉快的打表環節
{
    for(register int i=1;i<=maxn;i++)
    {
        vis[i]=check(i)?true:vis[i];
        if(vis[i])
        {
            if(i*2<=maxn)vis[i*2]=true;
            for(register int j=1;i*(j*2+1)<=maxn;j++)
                vis[i*(j*2+1)]=true;
            continue;
        } else excel.push_back(i);
    }
}
int main()
{
    #ifndef ONLINE_JUDGE//養成檔案IO的好習慣
    freopen("number.in","r",stdin);
    freopen("number.out","w",stdout);
    #endif
    Hit_The_Excel();
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        vector<int>::iterator it=lower_bound(excel.begin(),excel.end(),n);//用迭代器表示位置
        if(it==excel.end()||(*it)!=n)//對方輸了,輸出-1
            puts("-1");
        else ++it,printf("%d\n",*it);//輸出下一個數
    }
    return 0;
}

題後閒話

看了一下這次\(NOIp\)的情況,廣西的1=線預測為125,也就是說做出這道題就大概率能拿2=了,哈哈!
所以我為什麼要放棄這次參加機會呢?如果參加,或許還可以撈個獎,可惜沒有可能了……
明年再戰吧!