牛客 數碼

數碼

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

思路
求l到r的個數 轉換爲求1到r的個數 減去 1到l-1的個數
可以看到 l和r的長度長達1e9 如果暴力算每個的話 光是枚舉x就要1e9
可能會想到枚舉約數,但是這樣也還不夠,複雜度還是高的批爆
枚舉約數是肯定沒錯的,問題是考慮如何去優化
可以考慮去枚舉以x爲最高位的 區間的約數的個數
比如求最高數碼x=1 枚舉的區間的約數就是 [1,2) [10,20) [100,200) [1000,2000)…
這樣有個好處 就是最高位的數都是一樣的 就不需要特意去計算了

這樣就夠了嗎?遠遠不夠 就拿[1e8,2e8)這個區間來說 跑完這個 1s估計也用完了

其實到這裏這題也快結束了,還差最後一個優化點,也就是最重要的一點 下面的內容都是爲了說這個優化點 也就是除法分塊
對於一個數a來說,a除以一個數b的值如果要想少一,其中除數b需要成倍成倍的往上增加
看不太明白? 沒關係請繼續往下看

下面舉個例子來說 比如r=83 我們要求最高位爲1的約數的個數 這裏模擬一下過程

首先r/1=83 因爲每個數都是1的倍數 這是第一個區間[1,2)
接下來第二個區間 [10,20) 我們計算 a/10=83/10=8,意思是83內有8個數是10的倍數
接下來注意了 我們計算83/8=10,這裏計算的是乘8後≤r的最大值是多少
也就是對於83而言 83除以[10,10]這個區間的任意一個數的值都爲8

這裏可能還看不出什麼來 繼續往下
a/11=83/11=7 83/7=11 同理 83除以[11,11]這個區間的任意一個數的值都爲7
a/12=83/12=6 83/6=13 同理 83除以[12,13]這個區間的任意一個數的值都爲6
那麼我就不需要在計算13的情況了 因爲對於12到13 整除83後都等於 6 也就是說
83內是12的倍數有6個 是13的倍數也有6個 那麼最高位是1的頻數就是 6 * (13-12+1)=12
**a/14=83/14=5 83/5=16 這樣的話區間就是 [14,16]了 下一個除數就跑到了17 **
a/17=83/17=4 83/4=21 區間就是[17,21],這裏注意一下 右邊界已經超過了19了 所以右邊界要注意比較 那麼到這裏區間[10,20)的過程就模擬完了
上面僅僅是爲了模擬一下過程 如果沒有get到優化點請繼續看下面的粗體部分

如果暴力進行的話[10,20)需要進行10次 而上述過程只用了4次
區間太小可能看不出來什麼優化
比如 如果這個a是3e8 我們計算[1e8,2e8)這個區間
3e8/1e8=3 3e8/3=1e8 區間就是隻有1e8自己一個值
3e8/(1e8+1)=2 3e8/2=1.5e8 這個區間[1e8+8,1.5e8]跨度整整達到了5e7之大
接下來3e8/(1.5e8+1)=1 3e8/1=3e8 此時區間更大了[1.5e8+1,3e8] 當然了 3e8>=2e8
所以區間應該是[1.5e8+1,2e8) 優化點有多大?暴力這個區間 高達1e8次 這個優化到了只需要3次

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll solve(ll r,ll x){
    ll ans=r/x;
    ll st=x*10,en=min(r,x*10+9);
    for(;st<=r;st*=10,en=en*10+9){
        ll k=min(en,r);
        for(ll i=st;i<=k;){
          ll sum=r/i;
          ll mx=min(r/sum,k);
          ans+=sum*(mx-i+1);
          i=mx+1;
        }
    }
    return ans;
}
int main(){
    ios::sync_with_stdio(0);
    ll l,r;cin>>l>>r;
    for(int i=1;i<=9;i++){
        cout<<solve(r,i)-solve(l-1,i)<<endl;
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章