Investigating Div-Sum Property UVA - 11361(数位DP) 题解


link:https://vjudge.net/problem/UVA-11361



An integer is divisible by 3 if the sum of its digits is also divisible by 3. For example, 3702 is divisible
by 3 and 12(3+7+0+2) is also divisible by 3. This property also holds for the integer 9.
In this problem, we will investigate this property for other integers.
Input
The first line of input is an integer T (T < 100) that indicates the number of test cases. Each case is
a line containing 3 positive integers A, B and K. 1 ≤ A ≤ B < 2
31 and 0 < K < 10000.
Output
For each case, output the number of integers in the range [A, B] which is divisible by K and the sum
of its digits is also divisible by K.
Sample Input
3
1 20 1
1 20 2
1 1000 4
Sample Output
20
5
64


题目很短,题意也很简单,要求给定数字范围内的满足整除k且每一位数字加起来也整除k的数字的数量。用dp[i][j][l]表示位数为i,数位和mod k等于j,数字mod k等于l的数字的数量,因为每一次的k都不一样,所以似乎用dfs求dp会比递推好点。题目的k范围为10000,但是数字的上限为2^31,所以数位和最多也不会超过83,dp数组的后两维只需要开这么大就行。在数位统计的时候从高位到低位计数的同时要记录高位固定为最大时的数位和m1和数字和m2,然后枚举当前位x寻找对应位数dp[i][j][l]满足(j+m1+x)%k==0且(l+m2+x*10^i)%k==0的dp值,所以
ans += dfs(i, (K - (m1 + j)%K) % K, (K - (m2 + poww[i] *j)%K)%K);
然后记忆化搜索。

#include<iostream>
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<vector>
#include<string>
#include<queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const int maxe = 30005;
const int maxn = 10000;
const int maxk = 505;
const int mod = 1000000000;
int n,m,K;
int poww[12];
int dp[12][105][105];//dp[i][j][k]位数位i,位数字和mod k为j,数字modk为k的数字的数量
int dfs(int a, int m1, int m2){
    if (a == 0)return (m1 == 0 && m2 == 0) ? 1 : 0;
    if (dp[a][m1][m2] >= 0)return dp[a][m1][m2];
    dp[a][m1][m2] = 0;
    for (int i = 0; i < 10; i++){
        dp[a][m1][m2] += dfs(a - 1, ((m1 - i)%K + K) % K, ((m2 - i*poww[a-1])%K + K) % K);
    }
    return dp[a][m1][m2];
}

int solve(int n){
    if (n == 0){ return 1; }
    int nn = n, num[15], tot = 0,ans=0;
    while (nn != 0){
        num[tot++] = nn % 10;
        nn /= 10;
    }
    int m1 = 0, m2 = 0;
    for (int i = tot - 1; i >= 0; i--){
        if (i != 0){
            for (int j = 0; j < num[i]; j++){
                ans += dfs(i, (K - (m1 + j)%K) % K, (K - (m2 + poww[i] * j)%K)%K);
            }
        }
        else{
            for (int j = 0; j <= num[i]; j++){
                ans += dfs(i, (K - (m1 + j) % K) % K, (K - (m2 + poww[i] * j) % K)%K);
            }
        }
        m1 += num[i];
        m2 += poww[i] * num[i];
    }
    return ans;
}

int main(){
    poww[0] = 1;
    for (int i = 1; i < 12; i++){ poww[i] = poww[i - 1] * 10; }
    int t;
    scanf("%d", &t);
    while (t--){
        scanf("%d%d%d", &n, &m, &K);
        if (K>85)printf("0\n");
        else{
            memset(dp, -1, sizeof(dp));
            printf("%d\n", solve(m) - solve(n - 1));
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值