【BZOJ 1833】【ZJOI 2010】[數位DP]count 數字計數

題目描述

給定兩個正整數a和b,求在[a,b]中的所有整數中,每個數碼各出現了多少次。

題目分析

膜膜膜PoPoQQQ大爺。
首先,我們可以把求[a,b]中間的數量改成[1,b]-[1,a-1]。
然後考慮怎麼求[1,x]。
首先,對於位數小於x的數字因爲每位數可以隨便取,所以每一個數字出現次數是一樣的(允許前導0)。我們不妨預處理gi 表示位數爲i 時數字出現的次數,gi=gi1×10+10i1 ,因爲對於數字的第i 位有10種取法,相應的前i1 位就會被重複計算10次,而新增的第i 位每一種數碼又會出現10i1 次。然而累加時還需要考慮前導0,我們可以先枚舉位數,對於0累加時,我們不考慮第i 位爲0:ans=gi1×9 ;其他的就是ans=gi1×9+10i1
現在考慮位數和x相同時怎麼做,我們還是從高到低枚舉每一位數,不妨設當前數爲now ,枚舉的是第pos 位,那麼增量就是t=10pos1 。若now+t<=x 時,因爲最高位沒有達到上限,因此前pos1 位可以隨便取,產生的貢獻是當前已確定的後幾位數中的數碼乘以10pos1 (出現次數),加上前pos1 位數不計前導0的gpos
然後就完了,唯一需要注意的是因爲枚舉每一位數時都沒有計算上限值,所以答案求出來是[1,x) 於是我們還是要計算[1,r+1)[1,l) 的值。

代碼

/**************************************************************
    Problem: 1833
    User: szpszp
    Language: C++
    Result: Accepted
    Time:4 ms
    Memory:1292 kb
****************************************************************/

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

#define MAXN
#define MAXM
#define INF 0x3f3f3f3f
typedef long long int LL;

template<class T>
void Read(T &x){
    x=0;char c=getchar();bool flag=0;
    while(c<'0'||'9'<c){if(c=='-')flag=1;c=getchar();}
    while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
    if(flag)x=-x;
}

LL g[20],powten[20];
int num[15];
int len;

void getnum(LL x){
    len=0;
    while(x)num[++len]=x%10,x/=10;
}

LL ans[10];
void Solve(LL x,LL flag){
    getnum(x);

    for(int i=1;i<len;++i){
        ans[0]+=g[i-1]*9*flag;//0不能放在末尾
        for(int j=1;j<10;++j)
            ans[j]+=(g[i-1]*9+powten[i-1])*flag;
    }

    LL now=powten[len-1],t=now;
    int pos=len-1;
    while(now<x){
        while(now+t<=x){
            LL tmp=now/t;
            while(tmp)ans[tmp%10]+=t*flag,tmp/=10;
            for(int i=0;i<10;++i)ans[i]+=g[pos]*flag;
            now+=t;
        }
        t/=10,--pos;
    }
}

void init(){
    memset(ans,0,sizeof(ans));

    g[0]=0,powten[0]=1;
    for(LL i=1;i<15;++i){
        powten[i]=powten[i-1]*10;
        g[i]=g[i-1]*10+powten[i-1];
    }
}

int main(){
    init();

    LL l,r;
    Read(l),Read(r);

    Solve(r+1,1);
    Solve(l,-1);

    for(int i=0;i<9;++i)
        printf("%lld ",ans[i]);
    printf("%lld\n",ans[9]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章