洛谷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;
}
重要的是自信,一旦有了自信,人就会赢得一切。