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;
}

猜你喜欢

转载自www.cnblogs.com/copperoxide/p/9476722.html
今日推荐