luoguP3330 [ZJOI2011] 看电影--组合数学--高精度

\(luoguP3330\) [ZJOI2011] 看电影

废了老命想题解

$$luogu$$

$$HZOI$$

题意

到了难得的假期,小白班上组织大家去看电影。但由于假期里看电影的人太多,很难做到让全班看上同一场电影。最后大家在一个偏僻的小胡同里找到了一家电影院,但这家电影院分配座位的方式很特殊,具体方式如下:

电影院的座位共有 \(K\) 个,并被标号为 \(1 \sim K\)。每个人买完票后会被随机指定一个座位,具体来说是从 \(1 \sim K\) 中等概率随机选取一个正整数,设其为 \(L\)

如果编号 \(L\) 的座位是空位,则这个座位就分配给此人,否则将 \(L\) 加一,继续前面的步骤;如果不存在编号 \(L\) 的座位,则该人只能站着看电影,即所谓的站票。

小白班上共有 \(N\) 人(包括小白自己),作为数学爱好者,小白想知道全班都能够有座位的概率是多少。

提示

对于 \(100 \%\) 的数据,\(1 \leq T \leq 50\)\(1 \leq N,K \leq 200\)

本题输出的数据可能较大。

样例输入

3
1 1
2 1
2 2

样例输出

1 1
0 1
3 4

题解

假解 ( 正解往下看 😃 )

其实刚开始我没注意样例,并天真的以为每一种不同的选择方式是因为最后的得数不同。

然后得到了一个 nb 的式子 :

\[\frac{C^n_k}{C^n_k+n-1} \]

并打了一个高精 \(GCD\) 总计 \(270\) 多行,并自信的测了一发样例——

\(\color{red}{WA}\) 了。

重新考虑了一下,模拟了第三组样例:

选取方式如下:( 在第 \(i\) 个位置的数表示坐在第 \(k_i\) 个位置上 , 标红则表示其不合法)

\(1 , 1\) || \(1 , 2\) || \(2 , 1\) || \(\color{red}{2,2}\)

所以 \(P(3) = \frac{3}{4}。\)

现在来考虑正解。

正解

如果 \(n > k\) 直接输出 0 1 注:一定要是这个,输出 0 k^n abcdef \(G\) ┏┛墓┗┓...(((m-__-)m))

现在考虑总选数,显然为 \(k^n\) .

每个人有 \(k\) 种选法, \(n\) 个人,乘法原理得到答案。

考虑合法解。

那我们考虑什么情况下他不合法,就是说你的这个位置往后(包括这个位置)全是有人的。

我们开 \(k + 1\) 个椅子并排成一个环,每个人坐下形成合法的的数目 (注意: \(\bf{不考虑编号}\) )

这个不考虑编号的意思是:

\(3\) 个椅子两个人坐,则:

\[(1,2) , (2,3) , (3,1) \]

均属于同一种情况。

显然答案为

\[(k + 1)^{ n - 1 } \]

那你现在已经坐好后,我们考虑空的位置。

对于我们现在给到的所有排列肯定都会有满足的情况吧 ( 显然 )

那么在每一个空着的座位,将他之前的一个位置看为 \(n\) (尾) , 将他之后的一位看做 \(1\) 则意味着不会有人坐不下了 (因为末尾之后是空的啊)

我们再考虑对于一种排列方法来说,现在 \(n\) 个人坐着,显然有 \(k + 1 - n\) 个空着的位置。

所以每一种排列的合法方案数为 $k - 1 + n $ , 所以总合法方案数为 \((k + 1 - n) ^ { n - 1 } \times ( k - n + 1 )\)

所以最后答案为:

\[\frac{(k + 1 - n) ^ { n - 1 } \times ( k - n + 1 )}{k^n} \]

考虑代码实现

我们很显然得到 \((k + 1) ^ {n - 1}\)\(k^n\) 是互素的那我们约分时只考虑 $ k + 1 - n $ 与 \(k^n\) 就可以了。

我们用高精模求出 $k^n \bmod ( k + 1 - n ) $ 求出此数与 \(k + 1 - n\)\(GCD\) ,上下约掉即可

\(code\)

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std ;
const int N = 1e6 + 10 ;
const int binary = 1e9 ;
int gcd(int a, int b) {
    if (b == 0)
        return a ;

    return gcd(b, a % b) ;
}
int lena, lenb ;
int T, n, K ;
int a[N], b[N] ;
int beta[N], lenbeta ;
inline void print(int alpha[], int len1) {
    cout << alpha[len1] ;

    for (int i = len1 - 1 ; i >= 1 ; -- i) {
        printf("%09lld", alpha[i]) ;
    }
}
inline void High_Precision_times(int alpha[], int &len1, int k) {
    int j ;
    int carry = 0 ;

    for (j = 1 ; j <= len1 || carry ; ++ j) {
        alpha[j] = alpha[j] * k + carry ;
        carry = alpha[j] / binary ;
        alpha[j] %= binary ;
    }

    while (alpha[j] == 0 && j > 1)
        j -- ;

    len1 = j ;
}
inline void High_Precision_div(int alpha[], int &len1, int k) {
    int carry = 0 ;

    for (int j = len1 ; j >= 1 ; -- j) {
        int div = carry * binary + alpha[j] ;
        carry = div % k ;
        alpha[j] = div / k ;
    }

    while (alpha[len1] == 0 && len1 > 1)
        len1 -- ;
}
inline int read() {
    int x = 0, f = 1 ;
    char c = getchar() ;

    while (c < '0' || c > '9') {
        c = getchar() ;
    }

    while (c >= '0' && c <= '9') {
        x = x * 10 + c - '0' ;
        c = getchar() ;
    }

    return x * f ;
}
signed main() {
#ifndef ONLINE_JUDGE
    freopen("1.in", "r", stdin) ;
    freopen("1.out", "w", stdout) ;
#endif
    T = read() ;

    while (T --) {
        n = read() ;
        K = read() ;
        memset(a, 0, sizeof(a)) ;
        memset(b, 0, sizeof(b)) ;

        for (int i = 1 ; i <= lenbeta ; ++ i)
            beta[i] = 0 ;

        lenbeta = 0 ;
        lena = lenb = 1 ;
        a[1] = 1 ;
        b[1] = 1 ;

        for (int i = 1 ; i <= n - 1 ; ++ i)
            High_Precision_times(a, lena, K + 1) ;

        for (int i = 1 ; i <= n ; ++ i)
            High_Precision_times(b, lenb, K) ;

        for (int i = 1 ; i <= lenb ; ++ i)
            beta[i] = b[i] ;

        lenbeta = lenb ;
        High_Precision_times(a, lena, K - n + 1) ;

        if (n > K) {
            cout << 0 << ' ' ;
            cout << 1 ;
            cout << '\n' ;
            continue ;
        }

        int j ;
        int bemod = K - n + 1, carry = 0 ;

        for (j = lenb ; j >= 1 ; -- j) {
            int div = carry * binary + beta[j] ;
            carry = div % bemod ;
            beta[j] = div / bemod ;
        }

        int d = gcd(carry, K - n + 1) ;
        High_Precision_div(a, lena, d) ;
        High_Precision_div(b, lenb, d) ;
        print(a, lena) ;
        cout << ' ' ;
        print(b, lenb) ;
        cout << '\n' ;
    }
}
posted @ 2024-03-18 15:42  HANGRY_Sol&Cekas  阅读(26)  评论(4编辑  收藏  举报