UVa 11361 Investigating Div-Sum Property

一个不错的数位dp入门题

求区间 [a,b] 中满足各个数位之和是k的倍数且这个数本身也是k的倍数的数的个数
数据范围 : 1a,b231,1k10000

一个不错的数位dp入门题

感觉这题如果要想清楚的话,还是要明确的定义一下dp的状态
dp[pos][m][ms] 为前缀剩下的长度为pos,前缀 modk 为m,前缀的数位和 modk 为ms的时候,接在那样的前缀的后面使得总的数字能符合题意的数的个数
(如果不能理解的话,可以多读两遍

这个题还有一个问题就是如果你dp的第三维是按照k的大小开的话,需要 O(log10(231)×k2) 的空间复杂度,显然是开不下的

但是仔细想想,其实第三维完全不用开这么大,因为虽然我们存的是 modk 的值,但是题目范围内的数的数位和其实只有 floor(log10(231))×990 ,所以第三维只要开到100就够了

其实这个题还有一个优化,只要k超过90,我们都可以 O(1) 时间内得出答案,留作习题(想一想,为什么

不管对不对,反正这就是我对数位dp的理解了(笑


                           我是代码的昏割线

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

const int maxn = 11234;
int k;
int dp[12][maxn][108];
int dig[12];

int dfs(int pos,int mod,int mods,bool lim)
{
    if(pos < 0) return mod==0 && mods==0;
    int &ndp = dp[pos][mod][mods];
    if(!lim && ndp!=-1) return ndp;
    int ret = 0;
    int bound = lim ? dig[pos] : 9;
    for(int i=0; i<=bound; i++)
    {
        ret += dfs(pos-1,(mod*10+i)%k,(mods+i)%k,lim && i == bound);
    }
    if(!lim)
        ndp = ret;
    return ret;
}

int cal(int n)
{
    int len = 0;
    while(n)
    {
        dig[len++] = n % 10;
        n /= 10;
    }
    return dfs(len-1,0,0,true);
}

int main()
{
    int T;
    int a,b;
    scanf("%d",&T);
    while(T-- && ~scanf("%d %d %d",&a,&b,&k))
    {
        memset(dp,-1,sizeof(dp));
        printf("%d\n",cal(b)-cal(a-1));
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值