bzoj1833: [ZJOI2010]count 数字计数

题意:

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

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

题解:

第一道数位dp题。。

统计出1-x内每种数码出现次数,由前缀和思想答案为f[b]-f[a-1]

f[x,y]表示当前考虑到第x位,数码y出现的次数,注意到数据范围应为ll

状态转移对除0外的数为f[x-1,y]*9+tp[i-1](意义即为当前为枚举1-9,后面出现y的次数+当前位为y)

对于0进行一些细节处理,即首位不能放0(但之后是可以的) #比如不要把0001这样的0统计进去

之后统计答案的时候,只需从高位到低位考虑

例如8765 只需先算1-7999 再算8000-8700。。。。。。以此类推

代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll n,m,dp[30][30],tp[30],a[30],b[30],pp[30];
char st[30];
void js(ll oo[30],ll x) {
    ll tmp[30];
    memset(tmp,0,sizeof(tmp));
    bool tt=false;
    while (x!=0) 
    {
        ll i=x,j=0;
        while (i>=10) i=i/10,j++;
        ll p=i;
        if (tt==false)
        {
            for (ll i=1; i<=p-1;i++)
            tmp[i]+=pp[j+1];
            for (ll i=1; i<=9; i++)
          tmp[i]+=p*dp[j][i];
          tmp[0]+=(p-1)*dp[j][0]+tp[j];
          tt=true;
      } else
      {
          for (ll i=0; i<=p-1;i++)
            tmp[i]+=pp[j+1];
         for (ll i=0; i<=9; i++)
          tmp[i]+=p*dp[j][i];
      }
      sprintf(st,"%lld",x);
        ll ii=0;
        for (ii=0; ii<=1ll*strlen(st)-2;ii++)
          if (st[ii+1]!='0') break;
        tmp[0]+=(x-p*pp[j+1]+1)*ii;
      tmp[p]+=x-p*pp[j+1]+1;
        x=x-p*pp[j+1];
    }
    memcpy(oo,tmp,sizeof(tmp));
};
int main() {
    freopen("noip.in","r",stdin);
    freopen("noip.out","w",stdout);
    pp[1]=1;
    for (ll i=1; i<=13; i++) {
        pp[i+1]=pp[i]*10;
        for (ll j=0; j<=9; j++)
            dp[i][j]=dp[i-1][j]*10+pp[i];
        tp[i]=dp[i-1][0]*9+tp[i-1];
    }
    tp[1]=1;
    cin>>n>>m;
    if (n!=1) js(a,n-1);
    js(b,m);
    for (ll i=0; i<=8; i++) cout<<(b[i]-a[i])<<" ";
    cout<<(b[9]-a[9]);
    return(0);
}

 

posted @ 2017-12-28 22:21  尹吴潇  阅读(138)  评论(0编辑  收藏  举报