UVA11361 Investigating Div-Sum Property DP

38 篇文章 0 订阅

统计大于等于a,小于等于b,每一位之和可以被k整除,且本身也能被k整除的数的数量。

用f(d,m1,m2)表示剩余d个数字,这d个数字之和模k为m1,这d个数字组成的整数倍k整除之后余m2, 这样的数字的数量,

求a与b之间这样数字的数量,我们转化问题为前缀和只差即 sum(b)-sum(a-1)  (具体求解过程的不同,此处可能会有小出入)。

状态方程 训练指南上面有一个小错误。

正确的状态方程应为

f(d,m1,m2)=sum{ f( d-1,(m1-x) mod k,(m2-x*10^(d-1)) mod k ) |x=0,1,2.......9}

注意当我们将星号限制在后面三位的时候,我第四位的枚举范围,应在原本第四位的范围之内。而之后的枚举过程中,由于第四位小于原本的第四位,所以低位可以从0枚举到9



11361 - Investigating Div-Sum Property

Time limit: 4.000 seconds

I

Next generation contest – 4

Time Limit – 4 secs

Investigating Div-Sum Property

 

 

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 AB and K. 1 <= A <= B < 2^31 and 0<K<10000.

Output

For each case, output the number of integers in the range [AB] which is divisible by K and the sum of 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

 

ProblemSetter: Sohel Hafiz
Special Thanks: Ivan Krasilnikov

 

#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cstdio>

using namespace std;

int a,b,k;
//数字和最多为1+9*9=82,如果MOD大于此值,一定无解
//这是重要隐含条件,否则数据范围过大,内存无法承受。
int dp[15][110][110];
int pw[11]={1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};

int dfs(int d,int m1,int m2)
{
    if(d==0) return (m1==0&&m2==0)?1:0;
    if(dp[d][m1][m2]>=0) return dp[d][m1][m2];
    dp[d][m1][m2]=0;
    //加法原理,同余原理
    for(int i=0;i<10;i++)
        dp[d][m1][m2]+=dfs(d-1,((m1-i)%k+k)%k,((m2-i*pw[d-1])%k+k)%k);
    return dp[d][m1][m2];
}

//计算小于n的满足条件的数
int func(int n)
{
    int d=0,m1=0,m2=0,a[15];
    if(n==0) a[d++]=0;
    while(n!=0)
    {
        a[d]=n%10;
        n/=10;
        d++;
    }
    int ans=0;
    for(int i=d-1;i>=0;i--)
    {
        if(i!=0)
        for(int j=0;j<a[i];j++)
        {
            ans+=dfs(i,(k-(m1+j)%k)%k,(k-(m2+pw[i]*j)%k)%k);
          //cout<<i<<" "<<(k-(m1+j)%k)%k<<" "<<(k-(m2+pw[i]*j)%k)%k<<endl;
        }
        else
        for(int j=0;j<=a[i];j++)//如果个位不特判,则算出的结果为<n的数量,n本身会被遗漏,但只要把输出换乘func(b+1)-func(a)效果相同
        {
            ans+=dfs(i,(k-(m1+j)%k)%k,(k-(m2+pw[i]*j)%k)%k);
          //cout<<i<<" "<<(k-(m1+j)%k)%k<<" "<<(k-(m2+pw[i]*j)%k)%k<<endl;
        }
        m1+=a[i];
        m2+=pw[i]*a[i];

    }
    return ans;
}

int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        cin>>a>>b>>k;
        if(k>85)
            printf("0\n");
        else{
            memset(dp,-1,sizeof(dp));
            printf("%d\n",func(b)-func(a-1));
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值