bzoj 1833: [ZJOI2010]count 数字计数(数位dp)

本文介绍了一个关于数字计数的问题,即在给定的两个正整数范围内,统计每一位数字0到9出现的总次数。通过使用数位DP的方法,该文详细解释了如何解决这一问题,并提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1833: [ZJOI2010]count 数字计数

Time Limit: 3 Sec   Memory Limit: 64 MB
Submit: 2759   Solved: 1221
[ Submit][ Status][ Discuss]

Description

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

Input

输入文件中仅包含一行两个整数a、b,含义如上所述。

Output

输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。

Sample Input

1 99

Sample Output

9 20 20 20 20 20 20 20 20 20

HINT

30%的数据中,a<=b<=10^6;
100%的数据中,a<=b<=10^12。

Source

[ Submit][ Status][ Discuss]

HOME   Back


题解:数位dp

枚举t=0..9

f[i][j][k][0/1][0/1][0/1] 表示第I为填数字j,1-i位中t出现的次数为k,是否卡上限,是否卡下限,是否1-i位为前导零。

最后统计ans+=f[cnt][j][k][0/1][0/1][0/1]*k

注意特判0的情况,不是前导零才计算。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 20
#define LL long long
using namespace std;
LL n,m,a1[N],b1[N],f[N][N][N][3][3][3],cnt,ans,num[N],size[N],g[N][N][N][3][3][3];
int main()
{
	scanf("%lld%lld",&n,&m);
	cnt=0;
	while (m)
	{
		a1[++cnt]=m%10;
		m/=10;
	}
	for (int i=cnt;i>=1;i--)
	 b1[cnt-i+1]=a1[i],a1[i]=0;
	size[0]=1;
	for (int i=1;i<=cnt;i++)
	 size[i]=size[i-1]*10;
	int t=0;
	while (n)
	 {
	 	t++;
	 	a1[cnt-t+1]=n%10;
	 	n/=10;
	 }
    for (int t=0;t<=9;t++)
    {
    	memset(f,0,sizeof(f));
	    for (int i=a1[1];i<=b1[1];i++)
	     f[1][i][i==t&&t!=0?1:0][i==a1[1]?1:0][i==b1[1]?1:0][i==0?1:0]=1;
	    for (int i=1;i<=cnt-1;i++)
	     for (int j=0;j<=9;j++)
	      for (int l=0;l<=12;l++)
	       for (int a=0;a<=1;a++)
	        for (int b=0;b<=1;b++)
	         for (int c=0;c<=1;c++)
	          if (f[i][j][a][b][c])
	          {
	          	if (a&&b)
	          	 for(int k=a1[i+1];k<=b1[i+1];k++)
	          	  if (!c||k!=0)
	          	   f[i+1][k][l+(k==t?1:0)][a&(k==a1[i+1]?1:0)][b&(k==b1[i+1]?1:0)][c&(k==0?1:0)]+=f[i][j][l][a][b][c];
	          	  else  f[i+1][k][0][a&(k==a1[i+1]?1:0)][b&(k==b1[i+1]?1:0)][c&(k==0?1:0)]+=f[i][j][l][a][b][c];
	          	else 
				if (!a&&!b)
	          	 for (int k=0;k<=9;k++)
	          	   if (!c||k!=0)
	          	    f[i+1][k][l+(k==t?1:0)][a&(k==a1[i+1]?1:0)][b&(k==b1[i+1]?1:0)][c&(k==0?1:0)]+=f[i][j][l][a][b][c];
	          	   else  f[i+1][k][0][a&(k==a1[i+1]?1:0)][b&(k==b1[i+1]?1:0)][c&(k==0?1:0)]+=f[i][j][l][a][b][c];
	          	else
	          	if (!a)
	          	 for (int k=0;k<=b1[i+1];k++)
	          	  if (!c||k!=0)
	          	   f[i+1][k][l+(k==t?1:0)][a&(k==a1[i+1]?1:0)][b&(k==b1[i+1]?1:0)][c&(k==0?1:0)]+=f[i][j][l][a][b][c];
	          	  else  f[i+1][k][0][a&(k==a1[i+1]?1:0)][b&(k==b1[i+1]?1:0)][c&(k==0?1:0)]+=f[i][j][l][a][b][c];
	          	else
	          	if (!b)
	          	  for (int k=a1[i+1];k<=9;k++)
	          	  if (!c||k!=0)
	          	   f[i+1][k][l+(k==t?1:0)][a&(k==a1[i+1]?1:0)][b&(k==b1[i+1]?1:0)][c&(k==0?1:0)]+=f[i][j][l][a][b][c];
	          	  else  f[i+1][k][0][a&(k==a1[i+1]?1:0)][b&(k==b1[i+1]?1:0)][c&(k==0?1:0)]+=f[i][j][l][a][b][c];
			  } 
	    ans=0;
		for (int i=0;i<=9;i++)
		 for (int j=1;j<=12;j++)
		  for (int a=0;a<=1;a++)
		   for (int b=0;b<=1;b++)
		    for (int c=0;c<=1;c++)
		     ans+=f[cnt][i][j][a][b][c]*(LL)j;
		if (t!=9) printf("%lld ",ans);
		else printf("%lld\n",ans);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值