[BZOJ1799][AHOI2009]同类分布(数位DP)
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 19
Sample 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; }