CODEFORCES 55D

问区间内有多少数能被这个数的每一位整除

#include <iostream>
#include <functional>
#include <algorithm>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <sstream>
#include <utility>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <limits>
#include <memory>
#include <string>
#include <vector>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <list>
#include <map>
#include <set>
using namespace std;

const int MAXN=25;
const int MOD=2520;//1~9的lcm为2520
long long dp[MAXN][MOD][48];
int index[MOD+10];//记录1~9的最小公倍数
int bit[MAXN];
int gcd(int a,int b)
{
    if(b==0)return a;
    else return gcd(b,a%b);
}
int lcm(int a,int b)
{
    return a/gcd(a,b)*b;
}

void init()
{
    int num=0;
    for(int i=1;i<=MOD;i++)
        if(MOD%i==0)
            index[i]=num++;
}
long long dfs(int pos,int preSum,int preLcm,bool flag)//到这一位,到当前和,公倍数,有无到头
{
    if(pos==-1)//如果到头了
        return preSum%preLcm==0;//并且能取模,就加一
    if(!flag && dp[pos][preSum][index[preLcm]]!=-1)//没到头并且记录过,记录的都是满的
        return dp[pos][preSum][index[preLcm]];//返回记录值
    long long ans=0;//记录答案
    int endn=flag?bit[pos]:9;//如果之前的没满,上界是9,否则上界就是当前位
    for(int i=0;i<=endn;i++)//对当前这位从0开始
    {
        int nowSum=(preSum*10+i)%MOD;//计算合
        int nowLcm=preLcm;//继承公倍数
        if(i)nowLcm=lcm(nowLcm,i);//计算公倍数
        ans+=dfs(pos-1,nowSum,nowLcm,flag && i==endn);//向下递归
    }
    if(!flag)dp[pos][preSum][index[preLcm]]=ans;//如果没到头,记录的都是满的
    return ans;//返回答案
}
long long calc(long long x)
{
    int pos=0;
    while(x)
    {
        bit[pos++]=x%10;
        x/=10;
    }
    return dfs(pos-1,0,1,1);
}
int main()
{
    int T;
    long long l,r;
    init();
    memset(dp,-1,sizeof(dp));
    scanf("%d",&T);
    while(T--)
    {
        scanf("%I64d%I64d",&l,&r);
        printf("%I64d\n",calc(r)-calc(l-1));
    }
    return 0;
}

posted on 2016-05-13 16:47  very_czy  阅读(193)  评论(0编辑  收藏  举报

导航