oneman233

P2602 [ZJOI2010]数字计数(数位dp)

题意简单明了:

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

显然要使用数位dp,数码的出现次数很显然满足前缀和的性质,所以只需要分别计算出\(a-1\)\(b\)的答案即可
这里也要使用经典套路,先把十进制数字离散化存在一个数组里,然后由高位向低位搜索

记忆化搜索里传进去五个参数:

**
digit:当前要求解的数码
len:数字的剩余长度
sum:目前求出的当前数码出现的次数
zero:是否含有前导0
small:前面有没有某一位小于上限
**

其他参见代码,一顿转移即可:

#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
const int N=25;

ll a,b;
int num[N];
int dp[N][N][2][2];

ll gao2(int digit,int len,ll sum,bool zero,bool small)
{
	ll ret=0;
	if(len==0) return sum;//len为0的时候不需要记忆,之后不会再次用到len为0的状态
	if(dp[len][sum][zero][small]!=-1) return dp[len][sum][zero][small];
	for(int i=0;i<10;++i)
	{
		if(!small&&i>num[len]) break;
		ret+=gao2(digit,len-1,sum+((!zero||i)&&i==digit),zero&&i==0,small||(i<num[len]));
	}
	dp[len][sum][zero][small]=ret;
	return ret;
}

ll gao(ll x,int digit)
{
	int len=0;
	while(x)
	{
		num[++len]=x%10;
		x/=10;
	}
	memset(dp,-1,sizeof(dp));
	return gao2(digit,len,0,1,0);
}

signed main()
{
	scanf("%lld%lld",&a,&b);
	for(int i=0;i<10;++i)
		printf("%lld ",gao(b,i)-gao(a-1,i));
	return 0;
}

注意的是不知道哪儿爆了int,开了define int long long才过

posted on 2019-11-08 16:07  oneman233  阅读(248)  评论(0编辑  收藏  举报

导航