[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

3

HINT

【约束条件】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;
}

 

posted @ 2018-03-26 10:33  HocRiser  阅读(248)  评论(0编辑  收藏  举报