luoguP3330 [ZJOI2011] 看电影--组合数学--高精度
\(luoguP3330\) [ZJOI2011] 看电影
废了老命想题解
题意
到了难得的假期,小白班上组织大家去看电影。但由于假期里看电影的人太多,很难做到让全班看上同一场电影。最后大家在一个偏僻的小胡同里找到了一家电影院,但这家电影院分配座位的方式很特殊,具体方式如下:
电影院的座位共有 \(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 的式子 :
并打了一个高精 \(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\) 个椅子两个人坐,则:
均属于同一种情况。
显然答案为
那你现在已经坐好后,我们考虑空的位置。
对于我们现在给到的所有排列肯定都会有满足的情况吧 ( 显然 )
那么在每一个空着的座位,将他之前的一个位置看为 \(n\) (尾) , 将他之后的一位看做 \(1\) 则意味着不会有人坐不下了 (因为末尾之后是空的啊)
我们再考虑对于一种排列方法来说,现在 \(n\) 个人坐着,显然有 \(k + 1 - n\) 个空着的位置。
所以每一种排列的合法方案数为 $k - 1 + n $ , 所以总合法方案数为 \((k + 1 - n) ^ { n - 1 } \times ( k - n + 1 )\)
所以最后答案为:
考虑代码实现
我们很显然得到 \((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' ;
}
}