[bzoj1833][DP]count 数字计数

来源:互联网 发布:淘宝上美瞳为什么便宜 编辑:程序博客网 时间:2024/06/01 19:28

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。

题解

真的是痛苦。。这题折磨了我三天
一开始想的是二维预处理+区间缩小。结果我发现二维的在缩小的情况下是无法转移的。。怎么办?一分钟后我发现可以换成三维
设num[i][j][k]为 位数为i的数字,以j开头中第k种数字有多少个
先想如何预处理吧。首先,每个num[i][j][j]肯定要赋值为10^(i-1)对吧。因为i-1位到i位中,肯定至少有10^(i-1)个数字是以j开头的。
预处理完后我们枚举i-1位。因为i位数字实际上就是i-1位这些数字+任意一个数字组合而来的。譬如34567这个五位数 实际上就是3+4567这个四位数组成的嘛。那么i-1位的结果肯定对于i是有用的。枚举i位数开头数字j,i-1位数开头数字k,继承状态l
num[i][j][l]+=num[i-1][k][l]
好了我们预处理完了。那么询问的区间很明显是可以用大区间减去小区间对吧。
做一遍0~r再做一遍0~l-1最后输出就行了
怎么计算?首先对于位数<边界数位数 的数字,我们直接加就好了
譬如23005,我们把1~4位数的结果当然全部都要先加起来嘛。然后再处理其他的
对于位数等于边界数位数的数。我们就要一点一点加了
还是上面这个23005的例子。我们已经预处理过num[5][1][k]了对吧。而且10000~19999这些数一定可以被加起来。那么就直接加咯
那20000~23005怎么办??观察一下。可以拆成四位数的方法0~3005对不对?只要加上以2开头的五位数的个数就好了啊
那么这样一层一层叠下去。。就ok了

#include<cstdio>#include<cstring>#include<cstdlib>#include<algorithm>#include<cmath>using namespace std;typedef long long LL;LL num[15][15][15];//i位 最高位为j 第k个数字出现次数 int s[15];int Get(LL u){    int ret=0;    while(u)    {        s[++ret]=u%10;        u/=10;    }    return ret;}LL pow(int u){    LL ret=1,a=10;    while(u!=0)    {        if(u%2==1)ret=ret*a;        a=a*a;u/=2;    }    return ret;}LL ans[15][2];void sol(LL u,int op){    if(u<0)return ;    int len=Get(u);LL tmp=u;    if(u!=0)ans[0][op]++;    for(int i=1;i<len;i++)        for(int j=1;j<=9;j++)            for(int k=0;k<=9;k++)ans[k][op]+=num[i][j][k];    for(int i=1;i<s[len];i++)        for(int k=0;k<=9;k++)ans[k][op]+=num[len][i][k];    ans[s[len]][op]+=tmp-(s[len]*pow(len-1))+1;    tmp-=(s[len]*pow(len-1));    for(int i=len-1;i>=1;i--)    {        for(int j=0;j<s[i];j++)            for(int k=0;k<=9;k++)ans[k][op]+=num[i][j][k];        ans[s[i]][op]+=tmp-(s[i]*pow(i-1))+1;        tmp-=(s[i]*pow(i-1));    }}int main(){    LL a,b;    scanf("%lld%lld",&a,&b);    int lenx=Get(a),leny=Get(b);    for(int i=1;i<=12;i++)        for(int j=0;j<=9;j++)        {            num[i][j][j]=pow(i-1);            for(int k=0;k<=9;k++)                for(int l=0;l<=9;l++)                    num[i][j][k]+=num[i-1][l][k];        }    sol(b,0);    sol(a-1,1);    for(int i=0;i<9;i++)printf("%lld ",ans[i][0]-ans[i][1]);    printf("%lld\n",ans[9][0]-ans[9][1]);    return 0;}
原创粉丝点击