1. 程式人生 > >[HAOI2010] 計數

[HAOI2010] 計數

continue 不一定 mes 無限 span 排列 一個數 main ...

題目描述

你有一組非零數字(不一定唯一),你可以在其中插入任意個0,這樣就可以產生無限個數。比如說給定{1,2},那麽可以生成數字12,21,102,120,201,210,1002,1020,等等。

現在給定一個數,問在這個數之前有多少個數。(註意這個數不會有前導0).

輸入輸出格式

輸入格式:

只有1行,為1個整數n.

輸出格式:

只有整數,表示N之前出現的數的個數。

輸入輸出樣例

輸入樣例#1:

1020

輸出樣例#1:

7

說明

n的長度不超過50,答案不超過2^63-1.

題解

組合數學(+數位DP?)

可以不省略前導\(0\),轉換為這個數的全排列有多少小於\(N\)

先遞推組合數,然後每次循環每一位可以是多少,累答案

累答案就先放0,在放1,一直到9.如果有m位,答案就是:$C(m, cnt_0) * C(m-cnt_0, cnt_1) * C(m-cnt_0-cnt_1, cnt_2) * ... $

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

typedef long long LL;

int n, cnt[10], num[55];
char s[55];
LL c[60][60], ans;

void init_c() {
    const int l = 50;
    for(int i = 0; i <= l; ++ i) {
        c[i][0] = c[i][i] = 1;
        for(int j = 1; j < i; ++ j)
            c[i][j] = c[i-1][j] + c[i-1][j-1];
    }
} 

LL _count(int m) {
    LL ans = 1;
    for(int i = 0; i < 10; m -= cnt[i ++])
        ans *= c[m][cnt[i]];
    return ans;
} 

int main() {
    cin >> s;
    n = strlen(s);
    init_c();
    for(int i = 0; i < n; ++ i) 
        cnt[num[i] = s[i] - '0'] ++;
    for(int i = 0; i < n; ++ i) {
        for(int j = 0; j < num[i]; ++ j) {
            if(!cnt[j]) continue;
            -- cnt[j];
            ans += _count(n - 1 - i);
            ++ cnt[j];
        }
        cnt[num[i]] --;
    }
    printf("%lld\n", ans);
    return 0;
}

[HAOI2010] 計數