【Codeforces Beautiful numbers】 數位dp + 離散

Volodya is an odd boy and his taste is strange as well. It seems to him that a positive integer number is beautiful if and only if it is divisible by each of its nonzero digits. We will not argue with this and just count the quantity of beautiful numbers in given ranges.

Input
The first line of the input contains the number of cases t (1 ≤ t ≤ 10). Each of the next t lines contains two natural numbers l i and r i (1 ≤ l i ≤ r i ≤ 9 ·1018).

Please, do not use %lld specificator to read or write 64-bit integers in C++. It is preffered to use cin (also you may use %I64d).

Output
Output should contain t numbers — answers to the queries, one number per line — quantities of beautiful numbers in given intervals (from l i to r i, inclusively).

Examples
Input
1
1 9
Output
9
Input
1
12 15
Output
2

題意:求出區間內滿足數本身對其所有數位都能整除的數的個數

思路(數位dp + 離散):

·會很自然地想到,可以通過求出當前數位湊成的數和數位的最小公倍數做比較,看看能不能整除,來判斷是否合法。這樣的話就要多加兩個變量,分別是當前湊出的數sum,以及目前前綴數位的最小公倍數pre。
·但是,難點就在於,我們在記憶化的時候,必須藉助dp[pos][sum][pre]的三維數組,表示pos位置下,湊到sum的數,同時前綴數位的最小公倍數是pre時滿足題意的數的數量,但是一個sum就是9e18肯定不行。那怎麼辦呢?
·從結論倒推,既然我們要比較的是最後sum%pre是否爲0,而pre是有個上限的(1-9都有,最小公倍數也就是2520),那麼我就直接將sum改成每次的sum%2520就好了,因爲最後的結果肯定是不會超過2520的,我多算那麼多sum還要費事去記錄。(或者說,滿足題意時最後sum肯定是pre的倍數,而2520肯定也是pre的倍數,那麼sum%2520%pre也是等於0的,即記錄sum%2520和最後的pre取餘比較即可)那麼數組的量級變成了dp[20][2520][2520]但是這樣還是不夠,因爲還太大了。那還能怎麼優化呢?這時候,我們發現數組中pre位上,2520個位置不是每個位置都有用的,1-9的最小公倍數最多隻有不超過50種,所以,我們可以用hashmap來記錄每個最小公倍數對應是第幾個最小公倍數,然後dfs內每次計算出最小公倍數時在hashmap裏面找對應的就好了。這樣數組就變成dp[20][2520][50],這樣就可以寫了。

AC代碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <map>
#include <algorithm>
#define maxn 3000
using namespace std;
typedef long long ll;
ll dp[65][maxn][50];
ll a[65];
map<ll,ll>  hashmap;
ll gcd(ll a, ll b)
{
    if(a<b) swap(a,b);
    if(!b) return a;
    return gcd(b, a%b);
}

ll dfs(int pos, ll sum , ll pre, /*bool lead,*/ bool limit)
{
    if(pos==-1)  return (sum%(pre)==0);
    if(!limit&&dp[pos][sum][hashmap[pre]]!=-1) return dp[pos][sum][hashmap[pre]];
    ll up = limit?a[pos]:9;
    ll ans = 0;
    for(ll i=0;i<=up;i++)
    {
        if(i==0)  ans += dfs(pos-1,sum*10%2520,pre,limit&&a[pos]==i);
        else ans += dfs(pos-1,(sum*10+i)%(ll)2520, pre*i/gcd(pre,i),  limit&&a[pos]==i);
    }
    if(!limit) dp[pos][sum][hashmap[pre]] = ans;
    return ans;
}

ll solve(ll x)
{
    int pos = 0;
    if(x==0) a[0] = 0, pos=1;
    else
    while(x)
    {
        a[pos++] = x % 10;
        x /=10;
    }
    return dfs(pos-1,0,1,true);
}
int main()
{
    int kase;
    cin>>kase;
    memset(dp,-1,sizeof(dp));
    for(ll i=1,j=0;i<=2520;i++)
    if((ll)2520%i==0) hashmap[i] = j++;
    while(kase--)
    {
        ll l,r;
        cin>>l>>r;
        cout<<solve(r)-solve(l-1)<<endl;
    }
    return 0;
}

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