CodeForces 55D 数位统计

a positive integer number is beautiful if and only if it is divisible by each of its nonzero digits.
问一个区间内[l,r]有多少个Beautiful数字
范围9*10^18

一个数字要被它的所有非零位整除,即被他们的LCM整除,可以存已有数字的Mask,但更好的方法是存它们的LCM{git[i]}

int MOD = LCM{1,2,9} = 5 * 7 * 8 * 9 = 2520

10以内的数字情况为2^3 , 3^2 , 5 , 7

所以最小公倍数组合的情况只有4*3*2*2 = 48

所以复杂度大概为19*2520*48*10(状态数*决策数)

 

View Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;

#define LL __int64
const int Mod = 2520;
LL dp[19][2520][48];
int git[2522];
int num[19];

int gcd(int a,int b) {
    int mod;
    while(b) {
        mod = a % b;
        a = b;
        b = mod;
    }
    return a;
}

int Lcm(int a,int b) {
    return a * b / gcd(a,b);
}

void init() {
    int num = 0;
    for(int i = 1; i <= Mod; i++) {
        if(Mod % i == 0)
            git[i] = num++;
    }
    memset(dp,-1,sizeof(dp));
}

LL dfs(int dep,int Sum,int preLcm,bool doing) { //doing记录是否满,即是否可以从0到9枚举
    if(dep == -1) 
        return Sum % preLcm == 0;
    if(!doing && dp[dep][Sum][git[preLcm]] != -1) //doing为满的时候可以存起来当成公共的状态使用,降低复杂度    
        return dp[dep][Sum][git[preLcm]];
    int end = doing? num[dep]:9;
    LL ans = 0;
    for(int i = 0; i <= end; i++) {
        int nowSum = (Sum * 10 + i) % Mod;
        int nowLcm = preLcm;
        if(i)
            nowLcm = Lcm(nowLcm,i);
        ans += dfs(dep - 1, nowSum, nowLcm, doing && end == i);
    }
    if(!doing)//记录一般情况,不要记录特殊情况
        dp[dep][Sum][git[preLcm]] = ans;
    return ans;
}

LL cal(LL x) {
    int i = 0;
    while(x) {
        num[i++] = x % 10;
        x /= 10;
    }
    return dfs(i-1,0,1,1);
}

int main() {
    int T;
    scanf("%d",&T);
    init();
    while(T--) {
        LL l, r;
        cin >> l >> r;
        cout << cal(r) - cal(l-1) << endl;
    }
    return 0;
}

 

posted @ 2013-04-08 21:09  gray035  阅读(172)  评论(0编辑  收藏  举报