组合数学 - Bus Number

题目链接: https://codeforces.com/problemset/problem/991/E

题意:

给你一个数字字符串, 长度不超过18位, 问多少个序列满足:

  • 字符串中所有出现过的数字在序列中至少出现一次
  • 字符串的某一数字的出现次数要大于等于序列中该数字出现的次数
  • 序列不能是 0 开头 (公交车牌号肯定不是0开头= =)

思路: 数字字符串的长度不超过18位, 可以考虑搜索出所有的数字组合情况, 然后求当前数字组合情况的方案数, 方案数可以先包含 0 开头的情况, 然后减去 0 开头的情况.

举个例子: 如果当前数字组合情况为: 0022569

那么当前的方案数为 C 7 2 ∗ C 5 2 ∗ C 3 1 ∗ C 2 1 ∗ C 1 1 C_7^2 * C_5^2 * C_3^1 * C_2^1 * C_1^1 C72C52C31C21C11, 那么 0 开头的方案数为 C 6 1 ∗ C 5 2 ∗ C 3 1 ∗ C 2 1 ∗ C 1 1 C_6^1 * C_5^2 * C_3^1 * C_2^1 * C_1^1 C61C52C31C21C11, 前者减去后者则为当前组合的方案数

AC代码

public class Main {
    
    
    public static final int M = 200;
    public static int mod = Integer.MAX_VALUE;
    public static long[] invFact = new long[M];
    public static long[] fact = new long[M];
    public static int[]num = new int[12];
    public static int[]tmp = new int[12];
    public static long ans = 0;

    public static void main(String[] args) {
    
    
        InputReader in = new InputReader();
        getInvArray();
        String s = in.next();
        Arrays.fill(num, 0);
        Arrays.fill(tmp, 0);
        for(int i = 0; i < s.length(); i++) {
    
    
            num[s.charAt(i) - '0']++;
        }
        dfs(0, 0);
        System.out.println(ans);
    }

    public static void dfs(int x, int count) {
    
    
        if(x == 10) {
    
    
            // 不考虑前导为0
            int tmpCount = count;
            long tmpp = 1;
            for(int i = 0; i < 10; i++) {
    
    
                if(tmp[i] > 0) {
    
    
                    tmpp *= C(tmpCount, tmp[i]);
                    tmpCount -= tmp[i];
                }
            }
            // 减去前导为0的情况
         // if(tmp[0] > 0) tmpp -= tmpp * tmp[0] / count;
            ans += tmpp;
            // 减去前导为0的情况
           if(tmp[0] > 0) {
    
    
                tmpp = 1;
                count--;
                for(int i = 0; i < 10; i++) {
    
    
                    if(tmp[i] > 0) {
    
    
                        tmpp *= C(count, i == 0 ? tmp[i] - 1 : tmp[i]);
                        count -= i == 0 ? tmp[i] - 1 : tmp[i];
                    }
                }
                ans -= tmpp;
            }
            return ;
        }

        if(num[x] == 0) {
    
    
            dfs(x + 1, count);
            return ;
        }
        for(int i = 1; i <= num[x]; i++) {
    
    
            tmp[x] = i;
            dfs(x + 1, count + i);
        }
    }

    public static long C(int n, int m) {
    
    
        return (fact[n] * invFact[m] % mod * invFact[n - m] % mod);
    }

    public static long ksm(long n, long m) {
    
    
        long ans = 1L;
        while(m > 0) {
    
    
            if((m & 1) == 1) {
    
    
                ans *= n;
                ans %= mod;
            }
            n = n * n % mod;
            n %= mod;
            m >>= 1;
        }
        return ans % mod;
    }

    public static long getInv(long x) {
    
    
        return ksm(x, (mod - 2) * 1L);
    }

    public static void getInvArray() {
    
    
        fact[0] = 1L;
        invFact[1] = invFact[0] = 1L;
        for(int i = 1; i < M; i++) {
    
    
            fact[i] = i * fact[i - 1] % mod;
        }
        for(int i = 2; i < M; i++) {
    
    
            invFact[i] = invFact[i - 1] * getInv(i * 1L) % mod; // 阶乘逆元
        }
    }
}
class InputReader {
    
    
    BufferedReader buf;
    StringTokenizer tok;
    InputReader() {
    
    
        buf = new BufferedReader(new InputStreamReader(System.in));
    }
    boolean hasNext() {
    
    
        while(tok == null || !tok.hasMoreElements()) {
    
    
            try {
    
    
                tok = new StringTokenizer(buf.readLine());
            } catch (Exception e) {
    
    
                return false;
            }
        }
        return true;
    }
    String next() {
    
    
        if(hasNext()) return tok.nextToken();
        return null;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_45560445/article/details/121086349