uva11361(数论DP)

该博客主要介绍了UVA11361题目,内容涉及数论和动态规划。讨论了如何统计在给定范围内,各位数字和及整个数能被k整除的数的数量。博客通过转化问题,提出了正确状态方程,并指出训练资料中的一个小错误,强调在枚举过程中需要注意的细节。

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

题意:


基本是看了大白上的思路,然后还参考了http://blog.youkuaiyun.com/lenleaves/article/details/9104417

统计大于等于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


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

using namespace std;

int a,b,k;
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];
}

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);
        }
        else
        for(int j = 0; j <=a[i]; j++) {
            ans+=dfs(i,(k-(m1+j)%k)%k,(k-(m2+pw[i]*j)%k)%k);
        }
        m1 += a[i];
        m2 += pw[i] * a[i];

    }
    return ans;
}

int main() {
    int t;
	scanf("%d", &t);
    while(t--) {
		scanf("%d%d%d", &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;
}




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值