给定两个正整数a和b,求在[a,b]中的所有整数中,0~9每个数字各出现了多少次。
首先这肯定是个数位DP,所以我们先写个记搜然后慢慢考虑怎么优化
然后你发现0~9分开统计你也只用算18次,然后就非常愉快的分开算
所以我们现在只需要求某个数字在[a,b]出现了多少次
那这就是很简单的问题了(并不,我才做了两道数位DP
我们记一个sum,表示这一位之前的数有几个是我们要找的数,最后搜到边界了就return sum(当前这个数字有sum个所求数字)。
然后dp也开成二维,分别以位数和sum作为两个维度。如果只有位数这一维,答案是会被重复统计的,千万小心。
然后因为我们要统计0的个数,所以前导零我们也要记一下。
大概就这些了,上代码:
#include
#define rint register int
#define ivoid inline void
#define iint inline int
#define ll long long
#define endll '\n'
using namespace std;
const int N=1e6+5;
const int M=3e3+5;
const int inf=0x3f3f3f3f;
ll m,n,o,p,q,s,t,u,v,w,l,r;
ll a[14],dp[14][14],ans,res,tot,num,sum,flag=-1;
inline ll rad()
{
ll x=0,f=1;char c;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*f;
}
inline ll dfs(int pos,bool lim,bool flag,int y,ll sum)
{
if(!pos)return sum;
if(!flag&&!lim&&dp[pos][sum])return dp[pos][sum];
ll tot=0;
int up=lim?a[pos]:9;
for(rint i=0;i<=up;i++){tot+=dfs(pos-1,lim&&i==a[pos],i==0&&flag,y,sum+((i==y)&&(i||(!flag))));}
if(!flag&&!lim)dp[pos][sum]=tot;
return tot;
}
inline ll solve(ll x,int y)
{
ll x1=x;int num=0;memset(dp,0,sizeof(dp));
while(x1){num++;a[num]=x1%10;x1/=10;}
return dfs(num,1,1,y,0);
}
int main()
{
// freopen("water.out","w",stdout);
l=rad();r=rad();
for(rint i=0;i<=9;i++)
cout<<solve(r,i)-solve(l-1,i)<<" ";
}
小声逼逼:数位DP一眼看过去都觉得不可做,多看几个题解怎么感觉全是套路……?