bzoj1833: [ZJOI2010]count 数字计数(数位dp)

  学习了一波记忆化数位dp后写这题...我好像写的都跟大家的不一样???跑的还挺快的???(其实全世界一样都是0ms...)

  题目大意:求[a,b]中0~9分别出现几次。(a,b<=10^12)

  dp[pos][num][sum]表示前pos位,求num出现几次,这个数前pos位num已经出现sum次,前导零也很好处理,套记忆化模板就行了。

  (太久没写博客了水一篇...(雾

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#define ll long long
using namespace std;
ll dp[20][20][20],a[20],l,r;
void read(ll &k)
{
    k=0;ll f=1;char c=getchar();
    while(c>'9'||c<'0')c=='-'&&(f=-1),c=getchar();
    while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();
    k*=f;
}
ll dfs(int pos,int num,ll sum,bool lead,bool limit)
{
    if(pos==0)return sum;
    if(!limit && !lead && dp[pos][num][sum]!=-1)return dp[pos][num][sum];
    int up=limit?a[pos]:9;
    ll ans=0;
    if(!lead||pos==1)ans+=dfs(pos-1,num,sum+(num==0),0,limit && a[pos]==0);
    else ans+=dfs(pos-1,num,sum,1,limit && a[pos]==0);
    for(int i=1;i<=up;i++)
    ans+=dfs(pos-1,num,sum+(num==i),0,limit && a[pos]==i);
    if(!limit && !lead)dp[pos][num][sum]=ans;
    return ans;
}
ll solve(ll x,int num)
{
    if(x<0)return 0;
    if(x==0)return num==0?1:0;
    int pos=0;
    while(x)
    {
        a[++pos]=x%10;
        x/=10;
    }
    return dfs(pos,num,0,1,1);
}
int main()
{
    read(l);read(r);
    memset(dp,-1,sizeof(dp));
    for(int i=0;i<=8;i++)
    printf("%lld ",solve(r,i)-solve(l-1,i));
    printf("%lld
",solve(r,9)-solve(l-1,9));
}
View Code