bzoj 2425 [HAOI2010]计数 数学

题面

题目传送门

解法

显然可以一位一位确定答案

假设在第\(i\)时已经比原数小了,那么答案就可以加上\(\frac{(n-i)!}{s_0!s_1!…s_9!}\)

但是因为\(n≤50\),所以可能会爆long long,需要高精度

然而我就十分sb地写了高精度……

其实并不需要用高精度啊

可以先确定0放置的方案,在剩下的里面1的方案,……

然后对这个东西不断乘起来就好了,就是一个组合数的问题

代码(sb的高精度)

#include <bits/stdc++.h>
using namespace std;
bool cmp(string x, string y) {
	if (x.size() != y.size()) return x.size() > y.size();
	return x >= y;
}
int sum[10];
string fac[110];
string operator - (string x, string y) {
	int l1 = x.size() - 1, l2 = y.size() - 1;
	for (int i = 1; i <= l1 - l2; i++) y = '0' + y;
	string ret = ""; int k = 0;
	for (int i = l1; i >= 0; i--) {
		int t = (x[i] - '0') - (y[i] - '0') - k;
		if (t < 0) t += 10, k = 1; else k = 0;
		ret = (char)(t + '0') + ret;
	}
	while (ret.size() > 1 && ret[0] == '0') ret.erase(ret.begin());
	return ret;
}
string operator * (string x, int y) {
	int l = x.size(), a[210] = {0};
	for (int i = 0; i < l; i++) a[l - i - 1] = x[i] - '0';
	for (int i = 0; i < l; i++) a[i] *= y;
	for (int i = 0; i <= 3 * l; i++)
		a[i + 1] += a[i] / 10, a[i] %= 10;
	string ret = "";
	for (int i = 0; i <= 3 * l; i++) ret = (char)(a[i] + '0') + ret;
	while (ret.size() > 1 && ret[0] == '0') ret.erase(ret.begin());
	return ret;
}
string operator / (string x, string y) {
	string ret = "", tmp = "";
	int len = x.size() - 1;
	for (int i = 0; i <= len; i++) {
		tmp = tmp + x[i]; int s = 0;
		while (tmp.size() > 1 && tmp[0] == '0') tmp.erase(tmp.begin());
		while (cmp(tmp, y)) tmp = tmp - y, s++;
		ret = ret + (char)(s + '0');
	}
	while (ret.size() > 1 && ret[0] == '0') ret.erase(ret.begin());
	return ret;
}
long long calc(int len) {
	string ret = fac[len]; int tot = 0;
	for (int i = 0; i <= 9; i++)
		ret = ret / fac[sum[i]], tot += sum[i];
	if (tot > len) return 0;
	ret = ret / fac[len - tot];
	long long s = 0;
	for (int i = 0; i < ret.size(); i++)
		s = s * 10 + ret[i] - '0';
	return s;
}
int main() {
	string st; cin >> st;
	int len = st.size();
	for (int i = 0; i < len; i++)
		if (st[i] != '0') sum[st[i] - '0']++;
	fac[0] = "1"; long long ans = 0;
	for (int i = 1; i <= len; i++) fac[i] = fac[i - 1] * i;
	for (int i = 0; i < len; i++) {
		for (int j = 0; j < st[i] - '0'; j++)
			if (sum[j] || j == 0) {
				if (j) sum[j]--;
				ans += calc(len - i - 1);
				if (j) sum[j]++;
			}
		if (st[i] != '0') sum[st[i] - '0']--;
	}
	cout << ans << "\n";
	return 0;
}

代码(正常的组合数)

#include <bits/stdc++.h>
using namespace std;
long long sum[10], c[65][65];
long long calc(int len) {
    long long ret = 1;
    for (int i = 1; i <= 9; i++)
        ret *= c[len][sum[i]], len -= sum[i];
    return ret;
}
int main() {
    string st; cin >> st;
    int len = st.size();
    for (int i = 0; i < len; i++)
        if (st[i] != '0') sum[st[i] - '0']++;
    for (int i = 0; i <= 60; i++) {
        c[i][0] = 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 = 0; i < len; i++) {
        for (int j = 0; j < st[i] - '0'; j++) {
            if (j) sum[j]--;
            ans += calc(len - i - 1);
            if (j) sum[j]++;
        }
        sum[st[i] - '0']--;
    }
    cout << ans << "\n";
    return 0;
}
posted @ 2018-08-14 18:52  谜のNOIP  阅读(114)  评论(0编辑  收藏  举报