【牛客】數碼(思維)

戳這裏呀~ 題目傳送門

題目描述:

給定兩個整數 l 和 r ,對於所有滿足1 ≤ l ≤ x ≤ r ≤ 10^9 的 x ,把 x
的所有約數全部寫下來。對於每個寫下來的數,只保留最高位的那個數碼。求1~9每個數碼出現的次數。

輸入描述:

一行,兩個整數 l 和 r (1 ≤ l ≤ r ≤ 10^9)。 輸出描述: 輸出9行。
第 i 行,輸出數碼 i 出現的次數。

示例1
輸入

1 4

輸出

4
2
1
1
0
0
0
0
0

  • 這是某位學長灰常辛苦嘔心瀝血給我講的,再次感謝~ 打卡第一天~

    首先分析一下思路:

  • 可以看出這道題的數去範圍很大達到1e9,所以不考慮暴力。

  • 先假設求數碼5在區間(l,r)出現的次數ans,即求在區間(l,r)中所有約數首位是5的個數,然後我們可以想到:可以首先求出(1,r)中數碼5出現的次數,然後減去(1,l-1)區間內5出現的次數,這樣即可得到ans。

  • 依舊先假設數碼爲5,則求區間內的首位爲5的個數,即爲求集合(集合內所有數皆在範圍內,且首位都爲5)

{
5;
50,51,52…58,59;
500,501,502…550…598,599;
5000,5001,5002…5550…5998,5999;
…}

內所有數的倍數之和;

可以看出來區間長度是9 ,99,999…遞增的。

  • 分析到這裏,我們可以想到可以在集合內的 每個小區間內枚舉每個數的約數 ,然後相加求得。
    代碼如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll solve(ll r,ll x)//r爲距離,x是1~9
{
    ll ans=r/x; //先計算區間內滿足x是因子的數字的個數;
    ll st=x*10,en=min(r,x*10+9);//滿足集合中的每個區間(st,en);
    for(;st<=r;st=st*10,en=en*10+9)
    {
    	ll k=min(r,en);//k防止越界;
    	ll sum;
    	for(ll i=st;i<=k;i++)
    	{
    		sum=r/i;
            ans=ans+sum;
		}        
    }
    return ans;
}
int main()
{
    ll l,r;
    cin>>l>>r;
    for(int i=1;i<=9;i++)
    {
        cout<<solve(r,i)-solve(l-1,i)<<endl;
    }
}


然後結果是這樣的:在這裏插入圖片描述
經過一次失敗的嘗試之後,我們發現還要繼續優化算法。

  • 我們可以先計算出在區間內某個數 i 爲因子時的個數 sum=R/i,然後再通過區間的右邊 R / sum 算出 滿足因子數也爲 sum的最大數 mx,然後我們可以知道區間 (i,mx) 內的所有數爲因子時的個數都爲 sum,然後通過ans=ans+sum計算可以得到最後的結果。

舉個例子:在區間(1,100900)內,當i=901時,可以算出901爲因子時符合倍數的個數sum=100900/901=111 ,然後可以算出mx=100900/111=909,所以在區間(901,909)中每個數作爲因子數滿足的倍數都爲111,我們就可以直接算出區間(901,909)滿足高位爲數碼9的個數爲111*9個,這樣就可以實現跳過一部分枚舉區間。

代碼如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll solve(ll r,ll x)//r爲距離,x是1~9約數的首位
{
    ll ans=r/x;//先計算區間內滿足x是因子的數字的個數;
    ll st=x*10,en=min(r,x*10+9);//滿足集合中的每個區間(st,en);
    for(;st<=r;st=st*10,en=en*10+9)
    {
        ll k=min(r,en);//k防止越界
        for(ll i=st;i<=k;)
        {
            ll sum=r/i;//計算出當i爲因子時符合倍數的個數
            ll mx=min(k,r/sum);//更新滿足r/i都等於sum區間的長度
            ans+=(mx-i+1)*sum;//這個區間內的數r/i都等於sum,所以直接一步算
            i=mx+1;//跳到下一個位置
        }
    }
    return ans;
}
int main()
{
    ll l,r;
    cin>>l>>r;
    for(int i=1;i<=9;i++)
    {
        cout<<solve(r,i)-solve(l-1,i)<<endl;
    }
}

代碼是白嫖某學長的哈哈哈https://blog.csdn.net/s1050712899/article/details/105800199

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章