CF 991E. Bus Number

Bus Number

题意

给定数字 \(n\) ,求有多少个数字 \(x\) 满足:

  1. \(n\) 的所有元素都在 \(x\) 中出现过。
  2. \(x\) 的所有元素都在 \(n\) 中出现过。
  3. \(x\) 不含前导零。

举例:\(n\) 为2028,那么 \(x\) 可以为 \(208,820, 2820\) 等。

其中 \(1 \le n \le 10^{18}\)

分析

dfs计数。假设可以含有前导零,那么变成可重组合排列问题,假设选出来的数字有 \(sum\) 个,每个数字出现了 \(tmp[i]\) 次,那么方案数一共是 \(\dfrac{sum!} {\sum^{9}_{i=0}tmp[i]!}\)

再考虑含有前导零的情况,如果选出来的数字含有0,那么把一个0放在开头,计算出有前导零的方案数,相减即为这个组合没有前导零的方案数量。

Code

#include <iostream>
#include <string>
using namespace std;

long long fac[20];
int cnt[10], tmp[10]; // count记录原序列每个数字的次数,tmp记录每次可能的答案的数字的次数
long long ret;

void dfs (int cur)
{
    if (cur == 10) // 已经搜索完前面9个数字
    {
        // 先考虑可以有前导零的情况
        int len = 0; // 搜索到的答案的位数
        for (int i = 0; i < 10; i ++ ) len += tmp[i];
        long long p = fac[len];
        for (int i = 0; i < 10; i ++ ) p /= fac[tmp[i]]; // 计算方案数
        // 考虑前导零,计算出有前导零的情况,相减
        if (tmp[0]) p -= (p * tmp[0] / len);
        ret += p; return ;
    }
    // 枚举当前数字的数量
    for (int i = 1; i <= cnt[cur]; i ++ )
    {
        tmp[cur] = i; dfs(cur + 1);
    }
    // 如果当前数字没有,还需要取搜索下一个数字
    if (!cnt[cur]) dfs(cur+1);
}

int main ()
{
    fac[0] = 1; for (int i = 1; i < 20; i ++ ) fac[i] = fac[i-1] * i;
    string s; cin >> s;
    for (int i = 0; i < s.size(); i ++ ) ++ cnt[s[i] - '0'];
    dfs(0); // 从0元素开始搜索
    cout << ret << endl;
    return 0;
}
posted @ 2021-11-20 15:41  Horb7  阅读(54)  评论(0编辑  收藏  举报