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

题目

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


分析

设 f [ i ] [ s ] [ m ] [ 0 / 1 ] 为 前 i 位 , 和 为 s , 这 个 数 m o d    s u m = 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 ) m o d    s u m ] [ 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 ] [ s u m ] [ 0 ] [ 0 ] + f [ n ] [ s u m ] [ 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));//前缀和
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值