uva11361 - Investigating Div-Sum Property 数位统计DP

本文介绍了一种使用数位DP(动态规划)的方法来解决一类特殊问题:找出指定范围内能被K整除且各位数字之和也能被K整除的整数数量。通过递归地构建数字并维护其各个部分的状态,可以高效地解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Aninteger is divisible by 3 if the sum of its digits is also divisible by 3. Forexample, 3702 is divisible by 3 and 12(3+7+0+2) is also divisible by 3. Thisproperty also holds for the integer 9.

Inthis problem, we will investigate this property for other integers.

Input

The first line of input is an integer T(T<100) thatindicates the number of test cases. Each case is a line containing 3 positiveintegers A, B and K.1 <= A <= B < 2^31 and0<K<10000.

Output

For each case,output the number of integers in the range [A, B] which is divisible by K and the sumof its digits is also divisible by K.

Sample Input

Output for Sample Input

3

1 20 1

1 20 2

1 1000 4

20

5

64


  区间[A,B]中有多少个数满足:这个数能被K整除并且这个数的所有位之和能被K整除。

  用dp[i][j][k]表示i位的数有多少个每位和被K除余j,这个数被K除余K。比如i=3的时候就是000-999,i=4的时候是0000-9999...

  如果dp求出来了就好做了,区间的话只要求出[0,A-1],[0,B]相减就可以了。对于某个数A,比如3210,每位和被K除余m,这个数被K除余n。那么最高位取0,1,2的时候,后面三位是可以任取的,因此一定有一个dp[3][m'][n']与之对应,当最高位取3的时候,接着递归210,(m-3%K+K)%K,(n-3*10^3%K+K)%K。

  dp也可以用记忆化搜索,也可以递推求,道理是一样的,位数从小到大,相当于每次在最高位多加一位。dp[i][j][k]+=dp[i-1][(j-x%K+K)%K][(k-x*Pow(i-1)%K+K)%K],(0<=x<=9)。

  余数递推的时候要注意小于0的情况。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <map>
#include <iostream>
#include <algorithm>
using namespace std;
int T,A,B,K,L,dp[12][112][10010];//位数,每位和对K余数,这个数对K余数
int bit(int n,int &m){  //m是顺便算出n的最高位
    int ret=0;
    while(n){
        m=n;
        ret++;
        n/=10;
    }
    return ret;
}
int Pow(int n){
    int ret=1;
    for(int i=0;i<n;i++) ret*=10;
    return ret;
}
void init(){
    memset(dp,0,sizeof(dp));
    dp[0][0][0]=1;
    for(int i=1;i<=L;i++)
        for(int j=0;j<K&&j<=i*9;j++)
            for(int k=0;k<K;k++)
                for(int x=0;x<=9;x++) dp[i][j][k]+=dp[i-1][(j-x%K+K)%K][(k-x*Pow(i-1)%K+K)%K];
}
int solve(int n,int k1,int k2){ //[0,n) 每位和除K余k1,这个数除K余k2 
    int m,p=bit(n,m),ret=0;
    if(!p) return 0;
    for(int i=0;i<m;i++) ret+=dp[p-1][(k1-i%K+K)%K][(k2-i*Pow(p-1)%K+K)%K];
    ret+=solve(n%Pow(p-1),(k1-m%K+K)%K,(k2-m*Pow(p-1)%K+K)%K);
    return ret;
}
int main(){
    //freopen("in.txt","r",stdin);
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&A,&B,&K);
        int ans,t;
        L=max(bit(A,t),bit(B,t));
        if(K>(bit(B,t))*9) ans=0;
        else{
            init();
            ans=solve(B+1,0,0)-solve(A,0,0);
        }
        printf("%d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值