cf/codeforces#369-E - ZS and The Birthday Paradox- 数学+gcd+逆元+勒让德定理

引理:


勒让德定理 :http://baike.baidu.com/link?url=mqSXfFsk18D6zM7C1IVMh11M-3PaDRwCzqnB2ThJFymE98UuTGjmStD_uyEiPUb3Fw7QihFVfZaFeGo8kCkSMK

     在正数n!的素因子标准分解式中,素数p的指数记作
   
,则
   
.
证明百科上面也有:
这个似乎很好理解啊。。。。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;


}







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值