[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);
}
posted @ 2021-11-27 16:22  小篪篪  阅读(29)  评论(0编辑  收藏  举报
Live2D