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/+
+++++++++++++++++++++++++++++++++++++++++++