码迷,mamicode.com
首页 > 其他好文 > 详细

数位DP(CodeForces 55D Beautiful numbers)

时间:2015-04-24 18:22:21      阅读:125      评论:0      收藏:0      [点我收藏+]

标签:

题目大意:

美丽数: 这个数字能整除他自己各个位上的非零数。

给你一个区间问其中的美丽数有多少个。

分析:dp[位数][各个位数上的最小公倍数][对2520取余后的值] = 满足条件数的个数

1. 我所得到的数字对 所有位数上的数字的最小公倍数取余 如果==0 说明是合法的

2. 1-9的最小公倍数是2520 所以我的到的数字先对2520取余,再对其各个位数上的数字取余,一样可以得到相同的结果。

3. 因为我们要开 dp[20][2520][2520] 的数组是要超内存的 所以我们要将 最后一维 给他离散化一下, 最后结果只有50个数字。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
#define MOD  2520
LL dp[20][MOD][52];
int bit[20], index[MOD+10];

void init()
{
    int i, num = 0;
    memset(dp, -1, sizeof(dp));

    for(i=1; i<=MOD; i++)
    {
        if( MOD%i == 0)
            index[i] = num ++;
    }
}

int gcd(int a,int b)
{
    return b == 0? a : gcd(b, a%b);
}

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

LL dfs(int pos,int preSum,int preLcm,int flag)
{
    if(pos == -1)
    {
        return preSum%preLcm == 0;
    }

    if(!flag && dp[pos][preSum][index[preLcm]] != -1)
        return dp[pos][preSum][index[preLcm]];

    LL ans = 0;
    int end = flag?bit[pos]:9;

    for(int i=0; i<=end; i++)
    {
        int nowSum = (preSum*10 + i)%MOD;
        int nowLcm = preLcm;

        if(i) nowLcm = lcm(nowLcm, i);

        ans += dfs(pos-1, nowSum, nowLcm, flag && i == end);
    }
    if(!flag)
        dp[pos][preSum][index[preLcm]] = ans;

    return ans;
}

LL solve(LL n)
{
    int len = 0;

    while(n)
    {
        bit[len++] = n%10;
        n /= 10;
    }
    return dfs(len-1,0,1,1);
}
int main()
{
    int t;
    LL a, b;
    init();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld%lld", &a, &b);

        printf("%lld\n", solve(b) - solve(a-1) );
    }
    return 0;
}

 

数位DP(CodeForces 55D Beautiful numbers)

标签:

原文地址:http://www.cnblogs.com/chenchengxun/p/4453776.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!