关闭

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

285人阅读 评论(0) 收藏 举报
分类:

引理:


勒让德定理 :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

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:187834次
    • 积分:8551
    • 等级:
    • 排名:第2267名
    • 原创:735篇
    • 转载:4篇
    • 译文:0篇
    • 评论:26条
    233
    文章分类
    最新评论