洛谷P2518 [HAOI2010]计数

这个题可以默认有前导0,然后那些位数比输入数据小的数就可以被一起统计了。

之后就从第一位开始往后面扫,对于每一位,看是否达到限制,没有的话剩下的数随便排列就行,那么此时我们需要解决的就是可重集的全排列问题;如果达到限制就继续考虑之后的一位。

下面重点说下怎么解决可重集的全排列问题:

\(S=\{n_1*a_1,n_2*a_2,\cdots,n_k*a_k\}\)是由\(n_1\)\(a_1\),\(n_2\)\(a_2\),...\(n_k\)\(a_k\)组成的多重集。则S的全排列个数为:

\[\frac{n!}{n_1!*n_2!*\cdots*n_k!} \]

这个式子可以等于:

\[C_n^{n_1}*C_{n-n_1}^{n_1+n_2}*\cdots*C_{n-\sum_{i-1}^{k-1}n_i}^{\sum_{i=1}^{k}n_ui} \]

因为最后数据保证答案不超过long long的范围,所以我们这样直接把答案计算出来就好了,就不用担心阶乘溢出了。

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 55;
ll cnt[10];
ll ans ;
char s[N] ;
ll C[N][N] ;
int main() {
    scanf("%s", s + 1) ;
    int n = strlen(s + 1);
    for(int i = 1; i <= n; i++) cnt[s[i] - '0']++;
    C[0][0] = C[1][0] = C[1][1] = 1;
    for(int i = 2; i < N; i++) {
        C[i][0] = 1;
        for(int j = 1; j < N; j++)
            C[i][j] = C[i - 1][j - 1] + C[i - 1][j] ;
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 0; j < s[i] - '0'; j++) {
            if(cnt[j] >= 1) {
                ll res = 1;
                cnt[j]--;
                ll sum = n - i;
                for(int k = 0; k <= 9; k++) res *= C[sum][sum - cnt[k]], sum -= cnt[k] ;
                cnt[j]++;
                ans += res;
            }
        }
        cnt[s[i] - '0']--;
    }
    cout << ans;
    return 0;
}

posted @ 2019-05-16 19:23  heyuhhh  阅读(180)  评论(0编辑  收藏  举报