1799: [Ahoi2009]self 同类分布
Time Limit: 50 Sec Memory Limit: 64 MB
Submit: 1635 Solved: 728
[Submit][Status][Discuss]Description
给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数。Input
Output
Sample Input
10 19Sample Output
3HINT
【约束条件】1 ≤ a ≤ b ≤ 10^18
Source
设计好状态后就是比较简单的数位DP了,实际上数位DP就是记录了中间状态的搜索。
首先枚举各个位上的数之和为mod,然后它的贡献是所有数位和为mod且这个数本身%mod=0的数的个数,设f[i][sm][md]表示考虑从高到低前i位,前i位的数位和为sm,数本身%mod=md的数的合法个数,最终贡献为f[len][0][0]。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rep(i,l,r) for (ll i=l; i<=r; i++)
typedef long long ll;
using namespace std; ll L,R,ans,mod,a[21],P[21],f[21][170][170]; ll dfs(ll x,ll sm,ll md,ll lim){
if (!x) return (sm==mod) && !md;
if (!lim && ~f[x][sm][md]) return f[x][sm][md];
ll res=0;
rep(j,0,(lim?a[x]:9)) res+=dfs(x-1,sm+j,(j*P[x-1]+md)%mod,lim && j==a[x]);
if (!lim) f[x][sm][md]=res;
return res;
} ll calc(ll n){
if (!n) return 0;
memset(f,-1,sizeof(f));
P[0]=1; rep(i,1,18) P[i]=(P[i-1]*10)%mod;
ll len=0; while (n) a[++len]=n%10,n/=10;
return dfs(len,0,0,1);
} int main(){
freopen("bzoj1799.in","r",stdin);
freopen("bzoj1799.out","w",stdout);
scanf("%lld%lld",&L,&R);
for (mod=1; mod<=162; mod++) ans+=calc(R)-calc(L-1);
printf("%lld\n",ans);
return 0;
}