#数位dp,卡常优化#jzoj 1664 洛谷 4127 codevs 2232 同类分布

本文介绍了一种使用数位动态规划(DP)的方法来解决特定数字范围内,找到所有符合条件的数字个数的问题。具体地,对于给定的两个数a和b,算法能够找出在[a,b]区间内,所有数字的各位数之和能被该数字本身整除的数的数量。通过定义状态f[i][s][m][0/1],其中i表示当前考虑的位数,s表示数字和,m表示当前数字对和的模,0/1分别表示是否达到上界,文章详细解释了状态转移方程,并提供了实现代码。

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

题目

给出两个数a,ba,ba,b,求出[a,b][a,b][a,b]中各位数字之和能整除原数的数的个数。


分析

设f[i][s][m][0/1]为前i位,和为s,这个数mod  sum=m,0表示没卡上界,1表示卡了上界。设f[i][s][m][0/1]为前i位,和为s,这个数\mod sum=m,0表示没卡上界,1表示卡了上界。f[i][s][m][0/1]i,smodsum=m01
f[i+1][s+k][(m∗10+k)mod  sum][c按位与(k==t[i])]+=f[i][s][m][c];f[i+1][s+k][(m*10+k)\mod sum][c按位与(k==t[i])]+=f[i][s][m][c];f[i+1][s+k][(m10+k)modsum][c(k==t[i])]+=f[i][s][m][c];
然后其实得到这个最后答案为f[n][sum][0][0]+f[n][sum][0][1]f[n][sum][0][0]+f[n][sum][0][1]f[n][sum][0][0]+f[n][sum][0][1],然后卡卡常就没什么了


代码(自行卡常)

#include <cstdio>
#include <algorithm>
#include <cstring>
typedef unsigned long long ull;
ull a,b,f[2][181][181][2];
ull answer(ull x){
	ull X=x; int t[21],n=0;
	while (X) t[++n]=X%10,X/=10; 
	std::reverse(t+1,t+1+n); ull ans=0;
	for (register int sum=1;sum<=n*9;sum++){
		memset(f[0],0,sizeof(f[0])); bool x=0;
		f[0][0][0][1]=1;
		for (register int i=0;i<n;i++){
			x^=1; memset(f[x],0,sizeof(f[x]));
		    for (register int s=0;s<=sum;s++)
		    for (register int m=0;m<sum;m++)
		    for (register int c=0;c<2;c++){
			    long long res=f[x^1][s][m][c];
			    if (!res) continue;//得不到答案
			    for (register int k=0;k<=(c?t[i+1]:9);k++){
				    if (s+k>sum) break;
				    else f[x][s+k][(m*10+k)%sum][c&(k==t[i+1])]+=res;
			    }
		    }
		}
		ans+=f[x][sum][0][0]+f[x][sum][0][1];
	}
	return ans;
}
int main(){
	scanf("%llu%llu",&a,&b);
	return !printf("%llu",answer(b)-answer(a-1));//前缀和
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值