cf55D——數位dp+離散化+構造

題目鏈接:https://vjudge.net/problem/CodeForces-55D

題目大意:

計算給定範圍中有多少個漂亮數。 

一個正整數是 漂亮數 ,當且僅當它能夠被自身的各非零數字整除。

題解:

首先要知道一個結論:一個數如果能被一些數整除,那麼一定會被這些數的最小公倍數整除。

因此我們可以求出lcm(1,2,3....9)=2520.

對於任何一個數 num,num = 2520k + num',我們有 num % 2520 % t = num' % t (t = 1,2,3, ... , 9),

具體證明:https://www.cnblogs.com/Blogggggg/p/7764023.html

這樣我們再維護數的時候就可以對2520取餘了。

因此定義狀態dp[pos][num][lcm]表示枚舉到pos位,前面數位的形成的數對2520取餘的結果num,以及前面數位的數形成最小公倍數lcm.

最後只需要判斷num%lcm==0.

但是這樣的需要的空間是20*2520*2520*8/1024/1024=968M大於給的262M內存了。

因此我們需要考慮把最後一維形成的最小公倍數離散化一下,因爲小於2520,且能整除2520的數只有48個。

這樣空間就存的下了。

代碼實現:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <vector>

using namespace std;

#define LL long long
const int MOD = 2520;
LL dp[20][50][2550];
int dis[20];
int Hash[2550];

LL gcd(LL a, LL b)
{
    return b?gcd(b,a%b):a;
}

LL dfs(int len, int num, int lcm, bool flag)
{
    if(-1==len) return num%lcm == 0;
    if(!flag && ~dp[len][Hash[lcm]][num]) return dp[len][Hash[lcm]][num];
    LL ans = 0;
    int end = flag?dis[len]:9;
    for(int i=0; i<=end; i++)
        ans += dfs(len-1, (num*10+i)%MOD, i?lcm*i/gcd(lcm,i):lcm, flag&&i==end);
    if(!flag) dp[len][Hash[lcm]][num] = ans;
    return ans;
}

LL solve(LL n)
{
    int pos = 0;
    while(n)
    {
        dis[pos++] = n%10;
        n /= 10;
    }
    return dfs(pos-1, 0, 1, 1);
}

int main()
{
    int T;
    scanf("%d", &T);
    int cnt = 0;
    for(int i=1; i<=MOD; i++)
        if(MOD%i == 0)
            Hash[i] = cnt++;
    memset(dp, -1, sizeof(dp));
    while(T--)
    {
        long long l, r;
        scanf("%lld%lld", &l, &r);
        printf("%lld\n", solve(r)-solve(l-1));
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章