1. 程式人生 > >CodeForces 55D - Beautiful numbers - [數位DP+離散化]

CodeForces 55D - Beautiful numbers - [數位DP+離散化]

一個數 nta 整數 it is while 超出 sts cif include

題目鏈接:https://cn.vjudge.net/problem/CodeForces-55D

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 li and ri (1?≤?li?≤?ri?≤?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 li to ri, inclusively).

Example

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

題解:

首先,有 X % a = [ X % ( b * a ) ] % a,其中a,b,X均為正整數;

證明:設X = m * a + n;(其中n<a)

   則 [ X % ( b * a ) ] % a

    = [ ( m * a + n ) % ( b * a ) ] % a

    = [ ( m * a ) % ( b * a ) + n % ( b * a ) ] % a

    = [ a * ( m % b ) + n % ( b * a ) ] % a

    = [ n % ( b * a ) ] % a  (因為n<a,所以必然n<b*a,所以n % ( b * a ) = n)

    = n % a  (因為n<a,所以n % a = n)

    = n = X % a

當我們想判斷一個數是否能被它的所有位上的非零數字整除時,可以判斷這個數是否能被它的所有位上的非零數字的最小公倍數整除;

也就是說,我們有一個數X,a = lcm( all non-zero digit(X) ),我們要判斷X % a == 0?

就可以先想出一個數字b * a,讓X先對 b * a 取模,再對a取模判斷,結果是一樣的;

那麽哪個數字b * a是通用的呢?顯然,lcm(1,2,3,…,9)就是最佳人選。

經計算lcm(1,2,3,…,9) = 2520,

也就是說,我們可以先在dfs過程中計算出某一個數X對2520的取模值mod,同時計算出某一個數X的所有數位上的數的最小公倍數lcm;

最後我們只要判斷mod % lcm == 0?即可判斷X是否整除lcm。

所以就不難設計出dfs(pos,mod,lcm,limit)函數;

然後,我們發現dp[pos][mod][lcm],規模約有4*(19*2520*2520) = 4*120657600 B ≈ 471318.75 KB,題目並不允許開這麽大的空間,所以需要離散化;

不難發現,某一個數X的所有數位上的數的最小公倍數lcm,其必須是2520的因數,而我們通過如下代碼:

void discretize()
{
    int cnt=0;
    for(int i=1;i<=MOD;i++)
        if(MOD % i == 0) ++cnt;
    cout<<cnt<<endl;
}

就能知道1~2520中只有48個數,是2520的因數。

然後,我們只要把這48個數,離散化編號即可,這樣dp數組lcm維度就可以從2520降到48,就不會超出題目規定的內存空間。

AC代碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD = 2520;
int dig[20];
int idx[MOD+3];
ll dp[20][2525][50];

inline ll gcd(ll m,ll n){return n?gcd(n,m%n):m;}
inline ll lcm(ll m,ll n){return m/gcd(m,n)*n;}
void discretize()
{
    int cnt=0;
    for(int i=1;i<=MOD;i++)
        if(MOD % i == 0)
        {
            idx[i]=++cnt;
            //cout<<"idx["<<i<<"]="<<idx[i]<<endl;
        }
    //cout<<cnt<<endl;
}

ll dfs(int pos,int mod,int Lcm,bool limit)
{
    if(pos==0) return mod%Lcm==0;
    if(!limit && dp[pos][mod][idx[Lcm]]!=-1) return dp[pos][mod][idx[Lcm]];

    int up=limit?dig[pos]:9;
    ll ans=0;
    for(int i=0;i<=up;i++)
    {
        if(i==0) ans+=dfs(pos-1,(mod*10+i)%MOD,Lcm,limit && i==up);
        else ans+=dfs(pos-1,(mod*10+i)%MOD,lcm(Lcm,i),limit && i==up);
    }

    if(!limit) dp[pos][mod][idx[Lcm]]=ans;
    return ans;
}
ll solve(ll x)
{
    int len=0;
    while(x)
    {
        dig[++len]=x%10;
        x/=10;
    }
    return dfs(len,0,1,1);
}

int main()
{
    discretize();
    int t;
    scanf("%d",&t);
    memset(dp,-1,sizeof(dp));
    while(t--)
    {
        ll l,r;
        scanf("%I64d%I64d",&l,&r);
        printf("%I64d\n",solve(r)-solve(l-1));
    }
}

CodeForces 55D - Beautiful numbers - [數位DP+離散化]