1. 程式人生 > >【BZOJ 1833】【數位DP】 ZJOI2010 count【求在[a,b]中的所有整數中,每個數碼(digit)各出現了多少次】

【BZOJ 1833】【數位DP】 ZJOI2010 count【求在[a,b]中的所有整數中,每個數碼(digit)各出現了多少次】

描述:

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

Day1

題意:

求[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;
}