Codeforces Beta Round #51 D. Beautiful numbers 数位dp

题目链接:

http://codeforces.com/problemset/problem/55/D

D. Beautiful numbers

time limit per test4 seconds
memory limit per test256 megabytes
#### 问题描述 > 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. #### 输入 > 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 should contain t numbers — answers to the queries, one number per line — quantities of beautiful numbers in given intervals (from li to ri, inclusively). ####样例输入 > 2 > 1 9 > 12 15

样例输出

9
2

题意

求[l,r]区间里面满足能被所有自己数位上的非0数整除的数有多少个。

题解

首先有一个想法是把这个数%2,3,4,,,9的余数都当成状态保存下来,但这一显然会爆炸。

那么能不能只选一个数呢?有!那就是2,3,,,9的最小公倍数2920,因为a%(k*b)%b==a%b,所以我们把数压到%2920是完全不会失真的,同时我们可以吧lcm相同的进行归类,lcm相同的放到一起处理,注意:2,,,9的任何组合的lcm总共只有48种!(即2920的约数个数)。所以我们只要吧数位分出48类处理即可,对每一类保留%2920后的值是多少。

dp[len][lc][mod]表示前len位,lcm的值为lc,且取模2920=mod的有多少个。

代码

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<ctime>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
#define X first
#define Y second
#define mkp make_pair
#define lson (o<<1)
#define rson ((o<<1)|1)
#define mid (l+(r-l)/2)
#define sz() size()
#define pb(v) push_back(v)
#define all(o) (o).begin(),(o).end()
#define clr(a,v) memset(a,v,sizeof(a))
#define bug(a) cout<<#a<<" = "<<a<<endl
#define rep(i,a,b) for(int i=a;i<(b);i++)
#define scf scanf
#define prf printf

typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<pair<int,int> > VPII;

const int INF=0x3f3f3f3f;
const LL INFL=0x3f3f3f3f3f3f3f3fLL;
const double eps=1e-8;
const double PI = acos(-1.0);

//start----------------------------------------------------------------------

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

VI lcm;
int mp[2555];
int arr[22],tot;
///ismax标记表示前驱是否是边界值
///iszer标记前驱是否是前导零
LL dp[22][48][2555];
LL dfs(int len,int lc, int mod,bool ismax) {
    if (len == 0) {
        ///递归边界
        return mod%lcm[lc]==0;
    }
    if (!ismax&&dp[len][lc][mod]>=0) return dp[len][lc][mod];
    LL res = 0;
    int ed = ismax ? arr[len] : 9;

    ///这里插入递推公式
    for (int i = 0; i <= ed; i++) {
        res+=dfs(len-1,mp[Lcm(lcm[lc],i==0?1:i)],(mod*10+i)%2520,ismax&&i == ed);
    }
    return ismax ? res : dp[len][lc][mod] = res;
}

LL solve(LL x) {
    tot = 0;
    while (x) { arr[++tot] = x % 10; x /= 10; }
    return dfs(tot, mp[1],0, true);
}

void pre(){
    for(int i=1;i<=2520;i++){
        if(2520%i==0){
            lcm.pb(i);
        }
    }
    for(int i=0;i<lcm.sz();i++){
        mp[lcm[i]]=i;
    }
}


int main() {
    pre();
    clr(dp,-1);
    int tc,kase=0;
    scanf("%d",&tc);
    while(tc--){
        LL L,R;
        scf("%lld%lld",&L,&R);
        prf("%lld\n",solve(R)-solve(L-1));
    }
    return 0;
}

//end-----------------------------------------------------------------------
posted @ 2016-09-25 20:27  fenicnn  阅读(129)  评论(0编辑  收藏  举报