Time Limit: 50 Sec
Memory Limit: 64 MB
Description
给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数。
HINT
1 ≤ a ≤ b ≤ 10^18
题目分析
注意到各位数字的总和范围在区间
[
1
,
19
∗
9
]
[1,19*9]
[1,19∗9]范围内
所以可以先枚举各位数字的总和(也即膜数),再进行数位DP
d
p
[
i
]
[
s
u
m
]
[
m
o
d
]
dp[i][sum][mod]
dp[i][sum][mod]表示 当前处理到第
i
i
i位,已填的数位数字总和为sum,对前面枚举的总和(膜数)取膜余数为mod 的数字个数
剩下就是比较基础的数位DP记搜套版了
lt DP(int len,int sum,int mod,int pre)
{
if(len==0) return (sum==SUM&&mod==0);
if(!pre&&dp[len][sum][mod]!=-1) return dp[len][sum][mod];
lt res=0,mx=pre?dig[len]:9;
for(int i=0;i<=mx;++i)
{
if(sum+i>SUM) break;
res+=DP(len-1,sum+i,(mod*10+i)%SUM,pre&&i==mx);
}
if(!pre) dp[len][sum][mod]=res;
return res;
}
lt solve(lt x)
{
lt len=0,res=0;
while(x)
{
dig[++len]=x%10;
x/=10;
}
for(int i=1;i<=len*9;++i)
{
SUM=i;
memset(dp,-1,sizeof(dp));
res+=DP(len,0,0,1);
}
return res;
}
完整代码
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long lt;
lt read()
{
lt f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int maxn=20;
lt A,B,SUM;
lt dig[maxn],dp[maxn][maxn*9][maxn*9];
lt qpow(lt a,lt k,lt p)
{
lt res=1;
while(k>0){
if(k&1) res=(res*a)%p;
a=(a*a)%p; k>>=1;
}
return res;
}
lt DP(int len,int sum,int mod,int pre)
{
if(len==0) return (sum==SUM&&mod==0);
if(!pre&&dp[len][sum][mod]!=-1) return dp[len][sum][mod];
lt res=0,mx=pre?dig[len]:9;
for(int i=0;i<=mx;++i)
{
if(sum+i>SUM) break;
res+=DP(len-1,sum+i,(mod*10+i)%SUM,pre&&i==mx);
}
if(!pre) dp[len][sum][mod]=res;
return res;
}
lt solve(lt x)
{
lt len=0,res=0;
while(x)
{
dig[++len]=x%10;
x/=10;
}
for(int i=1;i<=len*9;++i)
{
SUM=i;
memset(dp,-1,sizeof(dp));
res+=DP(len,0,0,1);
}
return res;
}
int main()
{
A=read();B=read();
printf("%lld",solve(B)-solve(A-1));
return 0;
}