kuangbin带你飞 - 专题十五 - 数位dp

https://vjudge.net/contest/70324

A - Beautiful numbers

统计区间内的,被数位上各个为零数字整除的数的个数。

暴力的数位dp写法,绝对会TLE的,因为这个要深入到每个数字的最后才能判断是否合法。因为记忆化的意义在询问不多的时候用处不大就去掉了。

#include<bits/stdc++.h>
using namespace std;
#define ll long long

int a[20];
int d[10];
ll dfs(int pos,ll sum,bool lead,bool limit) {
    //递归边界,最低位是0,那么pos==-1说明这个数枚举完了
    if(pos==-1) {
        for(int i=1; i<=9; i++) {
            if(d[i]) {
                if(sum%i)
                    return 0;
            }
        }
        return 1;
    }
    int up=limit?a[pos]:9;//根据limit判断枚举的上界up
    ll ans=0;
    //开始计数
    for(int i=0; i<=up; i++) { //枚举,然后把不同情况的个数加到ans就可以了
        //合法的状态向下搜索
        if(i)
            d[i]++;
        ans+=dfs(pos-1,sum*10ll+i,lead && i==0,limit && i==a[pos]);//最后两个变量传参都是这样写的
        if(i)
            d[i]--;
    }
    return ans;
}

ll solve(ll x) {
    //可能需要特殊处理0或者-1
    if(x<=0)
        return 1;

    int pos=0;
    while(x) { //把数位分解
        a[pos++]=x%10;//编号为[0,pos),注意数位边界
        x/=10;
    }

    memset(d,0,sizeof(d));
    return dfs(pos-1,0,true,true);//刚开始最高位都是有限制并且有前导零的,显然比最高位还要高的一位视为0嘛
}

int main() {

    int t;
    scanf("%d",&t);
    ll le,ri;
    while(t--) {
        scanf("%lld%lld",&le,&ri);
        printf("%lld\n",solve(ri)-solve(le-1));
    }
}
View Code

没想出来怎么解决,去查了题解,题解暗示说,这样是和最小公倍数有关的。好像的确很有道理,细节只能自己想了。

首先考虑1~9的最小公倍数,也就是 $1*2^3*3^2*5*7=2520$ ,题解提到一个充要条件,就是一个数假如要能被某些数整除,等价于被这些数的最小公倍数整除,这个充要条件的正确性可以由质因数分解得知,就是说这个数的质因数分解必须比他的各个数位的质因数分解“高”,也就比各个数位的质因数分解的“轮廓”也就是最小公倍数“高”。

猜你喜欢

转载自www.cnblogs.com/Yinku/p/10445989.html
今日推荐