【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;
}

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