2018 上海大都会赛 J、Beautiful Numbers(数位dp)

题目链接

题意:问[1,N]里多少个数能够被其数位数字之和整除。(N<=1e12)


扯到数位,求区间满足此特征数字的个数,典型数位dp!
我们统计12位数字全为9,也就是这些数的数位和有大小约束,不超过108.
所以这里我们的dp[i][sum][num][mod] 记录的是前12-i为数位的代表的10进制值%mod为sum,后i+1位数位之和为num且是sum变为0的取值方式的个数(即满足题意的数的个数)!看起来有点大,实际上测试T不超过100,且时限8s,证明此解法符合题意!

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <set>
#include <map>
#include <vector>
#define llt long long
using namespace std;


llt dp[15][120][120][120];// dp[i][j][k] 记录i层树中前面的数位乘本身进制%k的个数
int dight[50];
llt ddp(int dep,int num,int sum,int limit,int mod){
    if(dep==-1) return sum==0&&num==0;

    llt &k = dp[dep][num][sum][mod];
    if(k!=-1&&!limit) return k;

    llt ans = 0;
    int last = limit?dight[dep]:9;
    for(int i=0;i<=last&&i<=num;++i){
        ans += ddp(dep-1,num-i,(sum*10+i)%mod,limit&&(i==last),mod);
    //cout<<num<<" "<<mod<<" "<<ans<<endl;
    }
    if(!limit) k = ans;
    return ans;
}

llt solve(llt x){
    int dep=0;
    while(x){
        dight[dep++] = x%10;
        x /= 10;
    }

    llt ans = 0;
    for(int i=1;i<=108;++i)
        ans += ddp(dep-1,i,0,1,i);
    return ans;
}
int main(){
    int cas;
    memset(dp,-1,sizeof(dp));
    scanf("%d",&cas);
    for(int k=1;k<=cas;++k){
        llt x;
        scanf("%lld",&x);
        printf("Case %d: %lld\n",k,solve(x));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值