Description
到了难得的假期, 小白班上组织大家去看电影. 但由于假期里看电影的人太多, 很难做到让全班看上同一场电影, 最后大家在一个偏僻的小胡同里找到了一家电影院. 但这家电影院 分配座位的方式很特殊, 具体方式如下:
- 电影院的座位共有 \(k\) 个, 并被标号为 \(1 \ldots k\), 每个人买完票后会被随机指定一个座位, 具体来说是从 \(1 \ldots k\) 中等可能的随机选取一个正整数, 设其为 \(L\).
- 如果编号 \(L\) 的座位是空位, 则这个座位就分配给此人, 否则将 \(L\) 加一, 继续前面 的步骤.
- 如果在第二步中不存在编号 \(L\) 的座位, 则该人只能站着看电影, 即所谓的站票.
小白班上共有 \(n\) 人 (包括小白自己), 作为数学爱好者, 小白想知道全班都能够有座位的 概率是多少.
Input
输入文件第一行有且只有一个正整数 \(T\), 表示测试数据的组数. 第 \(2 - T+1\) 行, 每行 两个正整数 \(n, k\), 用单个空格隔开, 其含义同题目描述.
Output
输出文件共包含 \(T\) 行. 第 \(i\) 行应包含两个用空格隔开的整数 \(A, B\), 表示输入文件中的第 \(i\) 组数据的答案为 \(\frac{A}{B} (gcd(A, B) = 1)\).
Sample Input
3
1 1
2 1
2 2
Sample Output
1 1
0 1
3 4
Data Range
对于 100% 的数据, 满足:\(T \leq 50, n, k \leq 200\)
Explanation
题解已经写的很清楚了, 其实最开始这道题是 @Burst_Zero 告诉我的神题. 然而我这样想了, 然后用 Python 交了一发, 发现无限 WA, 就怀疑是不是自己写错了. 于是从 ZJOI 的官方数据里拿出数据测了一发, 发现所有点都是 AC, 故而开始怀疑 BZOJ 的 Bug 到底有多少~ 最后用了个高精度库交了一发, A 了 (QwQ)
下面是应该正确但是 BZOJ 不给过的 Python 代码:
primes = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61,
67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139,
149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199]
s_primes = set(primes)
def factor(a):
res = {}
for i in primes:
if i > a:
break
cnt = 0
while a % i == 0:
cnt += 1
a //= i
if cnt > 0:
res[i] = cnt
return res
def mult_factor(a, mul):
res = {}
for i in a:
res[i] = a[i] * mul
return res
def add_factor(a, b):
res = {}
for i in a:
res[i] = a[i]
for i in b:
if i not in res:
res[i] = 0
res[i] += b[i]
return res
def join_factor(a):
res = 1
for i in a:
res *= i**a[i]
return res
T = int(input())
for idx in range(0, T):
n, K = map(int, input().split(' '))
if n > K:
print(0, 1)
continue
elif n == 1:
print(1, 1)
continue
upper = add_factor(factor(K-n+1), mult_factor(factor(K+1), n-1))
lower = mult_factor(factor(K), n)
for i in lower:
a = lower[i]
if i in upper:
b = upper[i]
c = min(a, b)
lower[i] -= c
upper[i] -= c
pass
u = join_factor(upper)
l = join_factor(lower)
print(u, l)
题解原文链接:http://blog.lightning34.cn/?p=166
概率就是可行方案除以所有方案.
所有方案就是 \(k^n\).
考虑可行方案, 如果加上一个座位使得所有的位置连成环的话, 方案数就是 \((k+1)^n\), 另外由于是环, 有重复的情况, 所以要除以 \(k+1\).
然后再断开, 只要是空位的都可以断开, 所以断开的方案数为 \(k-n+1\).
所以最后的可行方案数为 \((k−n+1) \cdot (k+1)^{n−1}\).
答案可以质因数分解之后高精度求.
Source Code
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long lli;
const int maxn = 47;
////////////////////////////////////////////////////////////////////////////////
// Thanks to the BigNum implementation by @PegasusWang on:
// http://www.cnblogs.com/PegasusWang/archive/2013/03/23/2977610.html
////////////////////////////////////////////////////////////////////////////////
#define MAXN 9999
#define MAXSIZE 10
#define DLEN 4
class BigNum
{
private:
int a[50000]; //可以控制大数的位数
int len; //大数长度
public:
BigNum(){ len = 1;memset(a,0,sizeof(a)); } //构造函数
BigNum(const int); //将一个int类型的变量转化为大数
BigNum(const char*); //将一个字符串类型的变量转化为大数
BigNum(const BigNum &); //拷贝构造函数
BigNum &operator=(const BigNum &); //重载赋值运算符,大数之间进行赋值运算
friend istream& operator>>(istream&, BigNum&); //重载输入运算符
friend ostream& operator<<(ostream&, BigNum&); //重载输出运算符
BigNum operator+(const BigNum &) const; //重载加法运算符,两个大数之间的相加运算
BigNum operator-(const BigNum &) const; //重载减法运算符,两个大数之间的相减运算
BigNum operator*(const BigNum &) const; //重载乘法运算符,两个大数之间的相乘运算
BigNum operator/(const int &) const; //重载除法运算符,大数对一个整数进行相除运算
BigNum operator^(const int &) const; //大数的n次方运算
int operator%(const int &) const; //大数对一个int类型的变量进行取模运算
bool operator>(const BigNum & T)const; //大数和另一个大数的大小比较
bool operator>(const int & t)const; //大数和一个int类型的变量的大小比较
void print(); //输出大数
};
BigNum::BigNum(const int b) //将一个int类型的变量转化为大数
{
int c,d = b;
len = 0;
memset(a,0,sizeof(a));
while(d > MAXN)
{
c = d - (d / (MAXN + 1)) * (MAXN + 1);
d = d / (MAXN + 1);
a[len++] = c;
}
a[len++] = d;
}
BigNum::BigNum(const char*s) //将一个字符串类型的变量转化为大数
{
int t,k,index,l,i;
memset(a,0,sizeof(a));
l=strlen(s);
len=l/DLEN;
if(l%DLEN)
len++;
index=0;
for(i=l-1;i>=0;i-=DLEN)
{
t=0;
k=i-DLEN+1;
if(k<0)
k=0;
for(int j=k;j<=i;j++)
t=t*10+s[j]-'0';
a[index++]=t;
}
}
BigNum::BigNum(const BigNum & T) : len(T.len) //拷贝构造函数
{
int i;
memset(a,0,sizeof(a));
for(i = 0 ; i < len ; i++)
a[i] = T.a[i];
}
BigNum & BigNum::operator=(const BigNum & n) //重载赋值运算符,大数之间进行赋值运算
{
int i;
len = n.len;
memset(a,0,sizeof(a));
for(i = 0 ; i < len ; i++)
a[i] = n.a[i];
return *this;
}
istream& operator>>(istream & in, BigNum & b) //重载输入运算符
{
char ch[MAXSIZE*4];
int i = -1;
in>>ch;
int l=strlen(ch);
int count=0,sum=0;
for(i=l-1;i>=0;)
{
sum = 0;
int t=1;
for(int j=0;j<4&&i>=0;j++,i--,t*=10)
{
sum+=(ch[i]-'0')*t;
}
b.a[count]=sum;
count++;
}
b.len =count++;
return in;
}
ostream& operator<<(ostream& out, BigNum& b) //重载输出运算符
{
int i;
cout << b.a[b.len - 1];
for(i = b.len - 2 ; i >= 0 ; i--)
{
cout.width(DLEN);
cout.fill('0');
cout << b.a[i];
}
return out;
}
BigNum BigNum::operator+(const BigNum & T) const //两个大数之间的相加运算
{
BigNum t(*this);
int i,big; //位数
big = T.len > len ? T.len : len;
for(i = 0 ; i < big ; i++)
{
t.a[i] +=T.a[i];
if(t.a[i] > MAXN)
{
t.a[i + 1]++;
t.a[i] -=MAXN+1;
}
}
if(t.a[big] != 0)
t.len = big + 1;
else
t.len = big;
return t;
}
BigNum BigNum::operator-(const BigNum & T) const //两个大数之间的相减运算
{
int i,j,big;
bool flag;
BigNum t1,t2;
if(*this>T)
{
t1=*this;
t2=T;
flag=0;
}
else
{
t1=T;
t2=*this;
flag=1;
}
big=t1.len;
for(i = 0 ; i < big ; i++)
{
if(t1.a[i] < t2.a[i])
{
j = i + 1;
while(t1.a[j] == 0)
j++;
t1.a[j--]--;
while(j > i)
t1.a[j--] += MAXN;
t1.a[i] += MAXN + 1 - t2.a[i];
}
else
t1.a[i] -= t2.a[i];
}
t1.len = big;
while(t1.a[len - 1] == 0 && t1.len > 1)
{
t1.len--;
big--;
}
if(flag)
t1.a[big-1]=0-t1.a[big-1];
return t1;
}
BigNum BigNum::operator*(const BigNum & T) const //两个大数之间的相乘运算
{
BigNum ret;
int i,j,up;
int temp,temp1;
for(i = 0 ; i < len ; i++)
{
up = 0;
for(j = 0 ; j < T.len ; j++)
{
temp = a[i] * T.a[j] + ret.a[i + j] + up;
if(temp > MAXN)
{
temp1 = temp - temp / (MAXN + 1) * (MAXN + 1);
up = temp / (MAXN + 1);
ret.a[i + j] = temp1;
}
else
{
up = 0;
ret.a[i + j] = temp;
}
}
if(up != 0)
ret.a[i + j] = up;
}
ret.len = i + j;
while(ret.a[ret.len - 1] == 0 && ret.len > 1)
ret.len--;
return ret;
}
BigNum BigNum::operator/(const int & b) const //大数对一个整数进行相除运算
{
BigNum ret;
int i,down = 0;
for(i = len - 1 ; i >= 0 ; i--)
{
ret.a[i] = (a[i] + down * (MAXN + 1)) / b;
down = a[i] + down * (MAXN + 1) - ret.a[i] * b;
}
ret.len = len;
while(ret.a[ret.len - 1] == 0 && ret.len > 1)
ret.len--;
return ret;
}
int BigNum::operator %(const int & b) const //大数对一个int类型的变量进行取模运算
{
int i,d=0;
for (i = len-1; i>=0; i--)
{
d = ((d * (MAXN+1))% b + a[i])% b;
}
return d;
}
BigNum BigNum::operator^(const int & n) const //大数的n次方运算
{
BigNum t,ret(1);
int i;
if(n<0)
exit(-1);
if(n==0)
return 1;
if(n==1)
return *this;
int m=n;
while(m>1)
{
t=*this;
for( i=1;i<<1<=m;i<<=1)
{
t=t*t;
}
m-=i;
ret=ret*t;
if(m==1)
ret=ret*(*this);
}
return ret;
}
bool BigNum::operator>(const BigNum & T) const //大数和另一个大数的大小比较
{
int ln;
if(len > T.len)
return true;
else if(len == T.len)
{
ln = len - 1;
while(a[ln] == T.a[ln] && ln >= 0)
ln--;
if(ln >= 0 && a[ln] > T.a[ln])
return true;
else
return false;
}
else
return false;
}
bool BigNum::operator >(const int & t) const
{
BigNum b(t);
return *this>b;
}
void BigNum::print()
{
int i;
cout << a[len - 1];
for(i = len - 2 ; i >= 0 ; i--)
{
cout.width(DLEN);
cout.fill('0');
cout << a[i];
}
// cout << endl;
}
////////////////////////////////////////////////////////////////////////////////
// End of BigNum library
////////////////////////////////////////////////////////////////////////////////
int primes[47] = { 46,
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
157, 163, 167, 173, 179, 181, 191, 193, 197, 199 };
class factor
{
public:
lli cnt[maxn];
factor(lli in) {
memset(cnt, 0, sizeof(cnt));
for (int i = 1; i <= primes[0]; i++) {
if (primes[i] > in) break;
while (in % primes[i] == 0)
in /= primes[i], cnt[i] += 1;
} return ; }
};
factor operator * (factor a, factor b)
{
factor c(1);
for (int i = 1; i <= primes[0]; i++)
c.cnt[i] = a.cnt[i] + b.cnt[i];
return c;
}
factor operator ^ (factor a, lli powr)
{
factor b(1);
for (int i = 1; i <= primes[0]; i++)
b.cnt[i] = a.cnt[i] * powr;
return b;
}
ostream& operator << (ostream& out, factor f)
{
bool printed = false;
for (int i = 1; i <= primes[0]; i++) {
if (f.cnt[i] <= 0) continue;
if (printed) out << "*";
printed = true;
out << primes[i] << "^" << f.cnt[i];
}
if (!printed) out << "1";
return out;
}
int T;
lli n, k;
int main(int argc, char** argv)
{
scanf("%d", &T);
for (int idx = 1; idx <= T; idx++) {
scanf("%lld%lld", &n, &k);
if (n > k) {
printf("0 1\n");
continue;
}
factor upper = factor(k-n+1) * (factor(k+1) ^ (n-1));
factor lower = factor(k) ^ n;
for (int i = 1; i <= primes[0]; i++) {
int a = upper.cnt[i], b = lower.cnt[i];
upper.cnt[i] -= min(a, b);
lower.cnt[i] -= min(a, b);
}
BigNum u = 1, l = 1;
for (int i = 1; i <= primes[0]; i++) {
for (int j = 1; j <= upper.cnt[i]; j++)
u = u * primes[i];
for (int j = 1; j <= lower.cnt[i]; j++)
l = l * primes[i];
}
u.print();
cout << ' ';
l.print();
cout << '\n';
}
return 0;
}