【试炼场】数字计数【数位DP】

传送门

题面描述

给定两个正整数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一眼看过去都觉得不可做,多看几个题解怎么感觉全是套路……?

你可能感兴趣的:(试炼场,DP)