【BZOJ 1833】【数位DP】 ZJOI2010 count【求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次】

传送门:1833: [ZJOI2010]count 数字计数

描述:

1833: [ZJOI2010]count 数字计数

Time Limit: 3 Sec   Memory Limit: 64 MB
Submit: 2766   Solved: 1226
[ 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

题意:

求[a,b]间所有的整数中0~9每个数字出现了几次

思路:

设dp[i][j][k]表示长度为i,开头为j的数中k的个数

分开统计答案,对于位数小于当前数的直接全部加上,剩余的拆分统计

代码:

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

ll a,b;
ll dp[maxn][maxn][maxn];//长度为i,开头为j的数中k的个数
ll bin[maxn];//i位中所有首位为i的数的个数
ll res[maxn];//记录0~9个数
int d[maxn];

void init(){//递推计算出每个整数
  bin[1]=1;
  for(int i=2; i<=13; i++)bin[i]=bin[i-1]*10;
  for(int i=0; i<=9; i++)dp[1][i][i]=1;
  for(int i=2; i<=13; i++)
    for(int j=0; j<=9; j++)
      for(int z=0; z<=9; z++){
        for(int k=0; k<=9; k++)
           dp[i][j][z]+=dp[i-1][k][z];
        dp[i][z][z]+=bin[i-1];
      }
}

void solve(ll x,int flag){ //计算1~x的所有整数
  int dnum=0;//记录当前数的位数
  ll tmpn=x;
  memset(d, 0, sizeof(d));
  while(x){ d[++dnum]=x%10; x/=10;}
  for(int i=1; i<=dnum-1; i++)//位数小于当前数的位数
    for(int j=1; j<=9; j++)
      for(int k=0; k<=9; k++)
        res[k]+=(dp[i][j][k]*flag);
  int tmp=dnum;
  while(tmp){//位数等于当前数的位数,拆分统计这里可以举一个120的例子仔细思考一下过程
    for(int i=0; i<d[tmp]; i++){
      if(!i && tmp==dnum)continue;//不能重复计算
      for(int j=0; j<=9; j++)
        res[j]+=(dp[tmp][i][j]*flag);
    }
    res[d[tmp]]+=(tmpn%bin[tmp]+1)*flag;
    tmp--;
  }
}

int  main(){  
/*  #ifndef ONLINE_JUDGE
  freopen("in.txt","r",stdin);
  #endif*/

  init();
  scanf("%lld%lld",&a,&b);
  solve(b, 1);solve(a-1, -1); 
  for(int i=0; i<=9; i++)
    printf("%lld%c",res[i],i==9?'\n':' ');
  return 0;
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值