[HAOI2010]计数题解


题目

??????????
????传????
????送????
????门????
??????????

题解

拿到这道题, 首先要明确一点, 这道题是一个毒瘤出的。
首先考虑所有可以生成且位数小于等于 \(ln(n)\) 的数有多少个。
接着考虑减去不符合要求的数。
不过为了防止算重, 我们考虑去掉的数为从第 \(j\) 位开始超过 \(n\)
这样就可以防止算重。
而我们这样定义以后, 就可以通过之前用了哪些数, 再枚举本位用的哪个数使其超过 \(n\) 的, 来计算

错点

这个题是真的坑, 连个模数都没有, 所以需要考虑用杨辉三角去预处理所有的 \(\dbinom{a}{b}\)

代码

//两个凡是: 
//凡是将难度反过来的都是毒瘤
//凡是卡常的都是毒瘤 

#include 
#include 
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);
}