Codeforces #55D-Beautiful numbers (数位dp)

D. Beautiful numbers
time limit per test
4 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

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).

Sample test(s)
input
1
1 9
output
9
input
1
12 15
output
2

题目链接:http://codeforces.com/problemset/problem/55/D

题意
找区间[l,r]中的B数:该数能被其每一位数字整除。

分析
能被每一位数字整除,则能被所有位上的数字的lcm(最小公倍数)整除。而1到9的lcm为2520,于是我们可以进行数位dp,dp[i][j][k]表示到第i位时,
该数为j,且此前各数位的lcm为k。可是很显然,数值j不能直接表示出来,我们得想个办法优化。因为递归到最后实质是求x%lcm,那么我们可以想到一个性质:
x%m==x%(km)%m,这样,x%lcm==x%2520%lcm,于是第二维就缩减到2520以内了。现在空间复杂度则为20*2520*2520,还是爆炸。思考一下哪里可以优化?
突然发现,真正作为最小公倍数的个数并没有那么多,只有48个。至此,空间复杂度就变成了20*2520*50,接下来就是记忆化递归求解了,要注意边界的情况。
详情看代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<bitset>
#include<map>
#include<deque>
#include<stack>
using namespace std;
typedef pair<int,int> pii;
#define X first
#define Y second
#define pb push_back
#define mp make_pair
typedef long long ll;
#define ms(a,b) memset(a,b,sizeof(a))
const int inf = 0x3f3f3f3f;
const int maxn = 1e5+5;
const int mod = 1e9+7;
const int mm = 2520;
#define lson l,m,2*rt
#define rson m+1,r,2*rt+1
ll dp[20][2550][50];
int has[2550];
int bit[20];
ll gcd(ll a,ll b){
    return (b==0)?a:gcd(b,a%b);
}
ll lcm(ll a,ll b){
    return a/gcd(a,b)*b;
}
void init(){
    int tot=0;
    for(int i=1;i<2550;i++){
        if(mm%i==0){
            has[i]=tot++;
        }
    }
}

ll dfs(int pos,int preSum,int preLcm,bool limit){
    if(pos<0) return preSum%preLcm==0; //到最后一位
    if(!limit && dp[pos][preSum][has[preLcm]]!=-1)
        return dp[pos][preSum][has[preLcm]];//记忆化
    ll ans=0;
    int ed = limit?bit[pos]:9; //该位有边界
    for(int i=0;i<=ed;i++){
        int nowSum = (preSum*10+i)%mm;
        int nowLcm = preLcm;
        if(i) nowLcm = lcm(nowLcm,i);
        ans += dfs(pos-1,nowSum,nowLcm,limit && i==ed);
    }
    if(!limit) dp[pos][preSum][has[preLcm]]=ans;
    return ans;
}
ll solve(ll x){
    int tot=0;
    while(x){
        bit[tot++]=x%10;
        x/=10;
    }
    return dfs(tot-1,0,1,1);
}

int main(){
#ifdef LOCAL
    freopen("in.txt","r",stdin);
#endif // LOCAL
    int t;
    ll l,r;
    ms(dp,-1);
    init();
    scanf("%d",&t);
    while(t--){
        cin>>l>>r;
        cout<<solve(r)-solve(l-1)<<endl;
    }
    return 0;
}
 

猜你喜欢

转载自www.cnblogs.com/fht-litost/p/8969723.html