传送门:1833: [ZJOI2010]count 数字计数
描述:
1833: [ZJOI2010]count 数字计数
Time Limit: 3 Sec Memory Limit: 64 MBSubmit: 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;
}