[HAOI2010]计数题解
题目
题解
拿到这道题, 首先要明确一点, 这道题是一个毒瘤出的。
首先考虑所有可以生成且位数小于等于 \(ln(n)\) 的数有多少个。
接着考虑减去不符合要求的数。
不过为了防止算重, 我们考虑去掉的数为从第 \(j\) 位开始超过 \(n\) 。
这样就可以防止算重。
而我们这样定义以后, 就可以通过之前用了哪些数, 再枚举本位用的哪个数使其超过 \(n\) 的, 来计算
错点
这个题是真的坑, 连个模数都没有, 所以需要考虑用杨辉三角去预处理所有的 \(\dbinom{a}{b}\)
代码
//两个凡是:
//凡是将难度反过来的都是毒瘤
//凡是卡常的都是毒瘤
#include <cstdio>
#include <algorithm>
using namespace std;
#define MAXN 10
#define MAXM 50
int s1[MAXN + 5];
int s2[MAXN + 5];
char sn[MAXM + 5];
long long C[MAXM + 5][MAXM + 5];
long long f (int *s) {
long long ans = 1;
int sum = 0;
for (int i = 0; i <= 9; i ++) {
if (s[i]) {
sum += s[i];
ans *= C[sum][s[i]];
}
}
return ans;
}
int main () {
char c = getchar ();
int n = 0;
while (c < '0' || c > '9') {
c = getchar ();
}
while (c >= '0' && c <= '9') {
s1[c ^ 48] ++;
s2[c ^ 48] ++;
sn[++ n] = c;
c = getchar ();
}
C[0][0] = 1;
for (int i = 1; i <= MAXM; i ++) {
C[i][0] = 1;
C[i][i] = 1;
for (int j = 1; j < i; j ++) {
C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
}
}
long long ans = 0;
for (int i = 1; i <= n; i ++) {
for (int j = (sn[i] ^ 48) + 1; j <= 9; j ++) {
if (s1[j]) {
s1[j] --;
ans -= f (s1);
s1[j] ++;
}
}
s1[sn[i] ^ 48] --;
}
ans --;
ans += f (s2);
printf ("%lld", ans);
}