引理:
勒让德定理 :http://baike.baidu.com/link?url=mqSXfFsk18D6zM7C1IVMh11M-3PaDRwCzqnB2ThJFymE98UuTGjmStD_uyEiPUb3Fw7QihFVfZaFeGo8kCkSMK
这个似乎很好理解啊。。。。n/2,代表1到n里有多少个数能整除2,n/4,代表有多少个数能整除两个2,n/8代表整除3个2。。。。以此类推。。。n/2+n/4+n/8....就是一共有多少个2能被整除
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Problem cf/codeforces#369-E - ZS and The Birthday Paradox-
题目大意
告诉你假设一年有2^n 天,k个人里有至少两个人在同一天生日的概率
假设该概率为p/q (最简形式),请输出p ,q 分别对 1e6+3取模后的值
数据范围:n , k <= 10^18。
解题分析
首先如果k>2^n ,答案为1/1
否则答案可以很容易推出是 1- (2^n-1)*(2^n-2)*....*(2^n-k+1)/(2^(nk))
假设算到了答案,那么我们得先约分啊,约分就得求gcd啊,显然下面分母是2的幂,gcd必然也是2^i,那么我们只要求出这个gcd,套个逆元暴力求分子分母就ok了。
怎么算gcd呢?既然gcd是2^i,那就算i即可,也就是计算(2^n-1)* ... *(2^n-k+1)含多少个因子2,
而这个就用到【勒让德定理】了,logk求出 k!里含多少个2.
知道了这个,用费马小定理,暴力求分子即可。
注意的是,分子最多有k项,k<=1e18,但是实际不用算那么多。
当k-1>=mod时,也就是说 某一项 (2^n%mod-i)==0 ,显然 该部分分子 实际为 (2^n-i),而其要对mod取模,如果前面的条件满足,则取模后必为零,答案就是0,可以不用再算了。所以要计算的分子最多就是min(k,mod)项
复杂度 min(mod,k)+ logk.logn
参考程序
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
const double pi=acos(-1.0);
double eps=0.000001;
typedef long long ll;
const int N=2*100000+50;
const ll mod=1e6+3;
long long powe_m(long long a,long long b )
{
long long ans=1;
long long tmp=a;
while(b!=0)
{
if (b&1)
ans=ans*tmp%mod;
tmp=tmp*tmp%mod;
b=b>>1;
}
return ans;
}
ll cal_gcd(ll k)
{
/* for(int i=0; i<k; i++) // (2^n-i)
if (i%2==0) num+=count_two_num(i);*/
ll num=0;
for (ll kk=k-1;kk;kk/=2) num+=kk/2;
return num;
}
int main()
{
ll n,k;
cin>>n>>k;
if (n<=63)
{
ll tmp=1LL<<n;
if (k>tmp)
{
printf("1 1\n");
return 0;
}
}
// ans = 1- A(2^n,k) / 2^(nk)
ll num= cal_gcd(k);
ll fenzi=1;
ll two_n=powe_m(2,n); //2^n
for(int i=1; i<k; i++)
{
fenzi=fenzi*(two_n-i)%mod; //A(2^n,k)
if (two_n==i) break; // if 2^n%mod==i, fenzi=0;
}
ll two_gcd=powe_m(2,num); //2^gcd
fenzi=fenzi*powe_m(two_gcd,mod-2)%mod;
ll fenmu=powe_m(two_n,k-1);
fenmu=fenmu* powe_m(two_gcd,mod-2)%mod;
fenzi=fenmu-fenzi;
if (fenzi<0) fenzi=(fenzi%mod+mod)%mod;
if (fenmu<0) fenmu=(fenmu%mod+mod)%mod;
printf("%lld %lld\n",fenzi,fenmu);
return 0;
}