BZOJ1833:[ZJOI2010]数字计数——题解

http://www.lydsy.com/JudgeOnline/problem.php?id=1833

https://www.luogu.org/problemnew/show/P2602

给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。

虽说是数位dp,但是用套路写的话蛮麻烦的……于是嫖了一篇记忆化搜索:https://www.cnblogs.com/Sakits/p/6815468.html

大致解释一下dfs的变量,p为当前填到哪里,num为数码,sum为当前答案。

lead为是否为数的第末位(就是数要比上限的长度短的情况)。

limit为是否卡着数的上限(如果你卡着上限的话你在枚举数码填进去的时候就会受到限制)。

我们记忆化仅仅当lead和limit均为0才行。

#include<cstdio>
#include<iostream>
#include<queue>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long ll;
const int N=15;
ll f[N][10][N],a[N];
ll dfs(int p,int num,int sum,bool lead,bool limit){
    if(!p)return sum;
    if(!limit&&!lead&&f[p][num][sum]!=-1)return f[p][num][sum];
    int maxx=limit?a[p]:9;
    ll ans=0;
    if(!lead||p==1)ans+=dfs(p-1,num,sum+(num==0),0,limit&&!a[p]);
    else ans+=dfs(p-1,num,sum,1,limit&&!a[p]);
    for(int i=1;i<=maxx;i++)
    ans+=dfs(p-1,num,sum+(num==i),0,limit&&a[p]==i);
    if(!limit&&!lead)f[p][num][sum]=ans;
    return ans;
}
ll dp(ll x,int y){
    int len=0;
    while(x)a[++len]=x%10,x/=10;
    if(!len)return y==0?1:0;
    return dfs(len,y,0,1,1);
}
int main(){
    ll a,b;
    scanf("%lld%lld",&a,&b);
    memset(f,-1,sizeof(f));
    for(int i=0;i<9;i++)printf("%lld ",dp(b,i)-dp(a-1,i));
    printf("%lld\n",dp(b,9)-dp(a-1,9));
    return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

 +本文作者:luyouqi233。               +

 +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

posted @ 2018-03-09 14:29  luyouqi233  阅读(221)  评论(0编辑  收藏  举报