0
点赞
收藏
分享

微信扫一扫

P2602 [ZJOI2010]數字計數

小_北_爸 2022-03-27 阅读 32

0-9,0-99,0-999,0-9999 ........

當中每一位數字出現的次數都是一樣的。

f[i] 對於i位數的數字,每個數字出現的次數,f[i] = f[i - 1] * 10 + 10^(i - 1)

但是這裡會有一個問題,就是前置0,因為對於上面的公式會把0001,0002,0003 當中出現的0也計算進0的出現次數中,所以最後我們要扣除這些前導0的出現次數。

假設說我們已經知道不同位數不同數字的出現次數,我們可以把一個4位數看成ABCD, 我們可以輕易算出A000中不同數字的出現次數,也就是f[i - 1] * A, 然後加上A還出現了 BCD + 1次,所以要加上BCD+1,然後我們還要加上BCD中 所有數字的出現次數,然後就是CD,然後就是D,問題的規模也就越變越小了。

答案要求我們求出[ a, b] ,我們可以簡單點先計算a - 1的答案,在計算 1到b的答案,最後相減就是區間a,b的答案了。

#include <iostream>
#include <cstring>
using namespace std;
long long a, b;
long long cnta[25], cntb[25];
long long f[25], ten[25];
void solve(long long x, long long *cnt){
    int len = 0;
    long long nums[25] = {0};
    while(x){
        nums[++len] = x%10;
        x/=10; 
    }
    //從A到D
    for(int i = len; i >= 1; i--){
        //A000所有字數出現次數
        for(int j = 0; j <= 9; j++){
            cnt[j] += nums[i] * f[i - 1]; 
        }
        //A000所有字數出現次數,j 一旦大於或等於A就停止
        for(int j = 0; j < nums[i]; j++){
            cnt[j] += ten[i - 1];
        }
        long long sum = 0;
        //計算BCD
        for(int j = i - 1; j >= 1; j--){
            sum = sum * 10 + nums[j];
        }
        cnt[nums[i]] += sum + 1;
        cnt[0] -= ten[i - 1];
    }
}
int main(){
    cin >> a >> b;
    memset(f, 0, sizeof(f)); memset(cnta, 0, sizeof(cnta)); memset(cntb,0,sizeof(cntb));
    ten[0] = 1;
    for(int i = 1; i <= 15; i++){
        f[i] = f[i - 1] * 10 + ten[i - 1];
        ten[i] = ten[i - 1]*10;
    }
    solve(a - 1, cnta);
    solve(b, cntb);
    for(int i = 0; i <= 9; i++)cout << cntb[i] - cnta[i] << " ";
    return 0;
}
举报

相关推荐

0 条评论