Codeforces Round #491 (Div. 2) . Bus Number

This night wasn't easy on Vasya. His favorite team lost, and he didn't find himself victorious either — although he played perfectly, his teammates let him down every time. He had to win at least one more time, but the losestreak only grew longer and longer... It's no wonder he didn't get any sleep this night at all.

In the morning, Vasya was waiting the bus to the university on the bus stop. Vasya's thoughts were hazy and so he couldn't remember the right bus' number quite right and got onto the bus with the number nn.

In the bus, Vasya thought that he could get the order of the digits in the number of the bus wrong. Futhermore, he could "see" some digits several times, but the digits he saw were definitely in the real number of the bus. For example, if Vasya saw the number 2028, it could mean that the real bus number could be 2028, 8022, 2820 or just 820. However, numbers 80, 22208, 52 definitely couldn't be the number of the bus. Also, real bus number couldn't start with the digit 0, this meaning that, for example, number 082 couldn't be the real bus number too.

Given nn, determine the total number of possible bus number variants.

Input

The first line contains one integer nn (1≤n≤10181≤n≤1018) — the number of the bus that was seen by Vasya. It is guaranteed that this number does not start with 00.

Output

Output a single integer — the amount of possible variants of the real bus number.

Examples

input

Copy

扫描二维码关注公众号,回复: 2345477 查看本文章
97

output

Copy

2

input

Copy

2028

output

Copy

13

Note

In the first sample, only variants 97and 79 are possible.

In the second sample, the variants (in the increasing order) are the following: 208, 280, 802, 820, 2028, 2082, 2208, 2280, 2802, 2820 8022, 8202, 8220.

题意:

       给你一个数,对于这个数它的数字的出现次数至少一次的子集的排列组合,不好理解,举个例子,2208这个数字,有2208和208的数字的排列组合的和。

思路:

     先了解一下多重集合的排列组合

多重集合的排列
定理:设S是多重集合,他有k种不同类型的对象,每一种类型的有限重复数是n1,n2,n3,…nk。设S的大小为n=n1+n2+n3+…nk。则S的n排列数目为n!/(n1!n2!n3!…nk!)

多重集合的组合
定理:设S是有k种类型对象的多重集合,每种元素均具有无限重复数。那么S的r组合的个数
=C(r+k-1,r)=C(r+k-1,k-1)

C(n, m) = n! / (m! * (n-m)!)

然后,对于这道题来说,对于一个数字如果他没有0的话,它的排列组合就是n!/(n1!n2!n3!…nk!),那如果有0的话,那么就把0作为第一位的排列数减掉,比如204这个数,全排列的话是3!/(1!* 1! * 1!) = 6,那么0作为第一位的排列组合数有多少呢,我们把0的次数-1,那么最终的结果就为2!/(1! * 1!) = 2, 这样把两个一减得到的4就是结果 204, 240, 402, 420

#include <iostream>
#include <string>
#include <string.h>
#include <set>
#include <algorithm>
#include <numeric>
using namespace std;
long long fact[30];
int c[10], c0[10];
string s;
set<string> have;
void splic(string x, int *c) {
    for (int i = 0; i < 10; i ++)
        c[i] = 0;
    for (char ch : x)
        c[ch - 48] ++;
}
long long getcount() {
    long long ans = fact[accumulate(c, c + 10, 0)];
    for (int i = 0; i < 10; i ++)//上述方法 n! / (n1! * n2! *...* nk!)
        ans /= fact[c[i]];
    return ans;
}
long long getans(string x) {
    splic(x, c);//对于每个测试,用c数组储存它每个数字的个数
    for (int i = 0; i < 10; i ++)//如果种类比c0的少,那么返回
        if (!c[i] && c0[i])
            return 0;
    sort(x.begin(), x.end());//对x排序
    if (have.count(x))//检查是否已经出现过了
        return 0;
    have.insert(x);
    long long ans = getcount();//不考虑前导0的所有排列
    if (c[0] > 0) {
        c[0] --;
        ans -= getcount();//考虑前导0,把前导0的个数-1,在减掉
    }
    return ans ;
}
int main() {
    cin >> s;
    splic(s, c0);//记录原始的每个数的个数用c0储存
    fact[0] = 1;
    for (int i = 1; i < 22; i ++)
        fact[i] = i * fact[i - 1];//每个数的阶乘
    int k = (int)s.length();//数字的长度
    long long ans = 0;
//    for (int i = 1; i <= (1 << k); i ++) {//这是一波很神奇的操作,我没看懂,不过它把这个数字所有的组合方案都求了出来,数字的个数从 1-k,有兴趣的可以自己试一试
//        string x;
//        for (int j = 0; j < k; j ++) {
//            if (i & (1 << j))
//                x += s[j];
//        }
//        cout << x << endl; //自己可以输出一下试试
//    }
    for (int i = 1; i <= (1 << k); i ++) {
        string x;
        for (int j = 0; j < k; j ++) {
            if (i & (1 << j))
                x += s[j];
        }
        ans += getans(x);//就个数
    }
    cout << ans << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/henu_jizhideqingwa/article/details/81130300