ZS and The Birthday Paradox

ZS and The Birthday Paradox

题目链接:http://codeforces.com/contest/711/problem/E

数学题(Legendre's formula)

这题是以生日悖论(如果有23个或23个以上的人,那么至少有两个人的生日相同的概率要大于50%)为背景的数学题。公式本身不难推,主要是对大数的处理。

首先,我们需要找到分子和分母的公因数,这里用到了Legendre's formula(举个例子:10!的因数中质数3的个数为[10/3+10/(3^2)])。因为若2^n-x与2^n有公因数,那么x与2^n有相同的公因数,所以求(2^n)(2^n-1)(2^n-2)*...*(2^n-k+1)与(2^n)^k的公因数,就转化为了求(k-1)!与(2^n)^k的公因数。

之后就是分别求分子和分母:

对于分母来说,只需要用快速幂(也可以用费马小定理)就可以很容易的求出,再乘上公因数的逆元即可;

而对于分子来说,稍微有点麻烦:

1.如果k>=M,即(2^n)(2^n-1)(2^n-2)*...*(2^n-k+1)中至少有连续的M个整数,

那么(2^n)(2^n-1)(2^n-2)*...*(2^n-k+1)一定为M的倍数,所以它被M求模后余0;

2.如果k<M,因为M很小,所以枚举一下,就可以求出分子,再乘上公因数的逆元即可。

总的时间复杂度为O(M+lgk+lgn)

(感谢游少半夜教我证明Orz)

证明:(A/B)modM=(A*(BmodM)')modM,其中B'为B在M下的逆元

令B=b1*b2*b3*...*bn,则((BmodM)')modM

=((b1*b2*b3*...*bn mod M)')modM

=(b1modM*b2modM*...*bn mod M)'modM

=(b1*b2*...*bn)modM*(b1modM*b2modM*...*bn mod M)'modM*(b1*b2*...*bn)'modM

=(b1modM*b2modM*...*bn mod M)*(b1modM*b2modM*...*bn mod M)'modM*(b1*b2*...*bn)'modM

=1*(b1*b2*...*bn)'modM=(b1*b2*...*bn)'modM

=b1*b1'modM*b2*b2'modM...bn*bn'mod M*(b1*b2*...*bn)'modM

=(b1*b2*...*bn)modM*(b1'modM*b2'modM...bn'mod M)*(b1*b2*...*bn)'modM

=b1'modM*b2'modM...bn'mod M

代码如下:

 1 #include<cstdio>
 2 #include<iostream>
 3 #define M (long long)(1e6+3)
 4 using namespace std;
 5 typedef long long LL;
 6 LL n,k,cnt,molecular,numerator,x,y;
 7 LL exGCD(LL a,LL b){
 8     if(b==0){
 9         x=1,y=0;
10         return a;
11     }
12     LL r=exGCD(b,a%b);
13     LL temp=x;
14     x=y;
15     y=temp-(a/b)*y;
16     return r;
17 }
18 LL mod(LL a,LL b){
19     LL base=a,temp=1;
20     while(b){
21         if(b&1)temp=(temp*base)%M;
22         base=(base*base)%M;
23         b>>=1;
24     }
25     return temp;
26 }
27 int main(void){
28     cin>>n>>k;
29     if(n<=63&&k>(((LL)1)<<n)){//总人数大于总天数
30         cout<<"1"<<" "<<"1"<<endl;
31         return 0;
32     }
33     LL temp=2;
34     while(k-1>=temp){//根据Legendre's formula,求出(k-1)!的因数中质数2的个数
35         cnt+=((k-1)/temp);
36         temp<<=1;
37     }
38     if(cnt){//若有公因数,则求出公因数的逆元
39         LL gcd=mod(2,cnt);
40         exGCD(gcd,M);
41         x=(x+M)%M;
42     }else x=1;//若没有公因数,则令x=1,来消除对后面计算的影响
43     temp=mod(2,n);//计算2^n
44     numerator=(mod(temp,k-1)*x)%M;//计算(2^n)^(k-1)
45     if(k>=M)molecular=0;//若分子出现连续的M个整数,则分子一定为M的倍数
46     else{
47         molecular=1;
48         for(LL i=1;i<k;++i){
49             molecular=((temp-i+M)%M*molecular)%M;//计算分子
50         }
51         molecular=(molecular*x)%M;//除以公因数
52     }
53     molecular=(numerator-molecular+M)%M;
54     cout<<molecular<<" "<<numerator<<endl;
55 }
原文地址:https://www.cnblogs.com/barrier/p/5828044.html