CF 991E. Bus Number
Bus Number
题意
给定数字 \(n\) ,求有多少个数字 \(x\) 满足:
- \(n\) 的所有元素都在 \(x\) 中出现过。
- \(x\) 的所有元素都在 \(n\) 中出现过。
- \(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;
}