YZOJ P4621 [CSP-S 2019 四校联训 Round 1]生日悖论
时间限制:1000MS 内存限制:131072KB
难度:\(5.0\)
- 
题目描述
一共有 \(N\) 种数字,所以按照包含数字的集合分类,可能有 \(2^N\) 类不同的集合。每个集合包含一种特定的数字的概率都是 \(\displaystyle\frac{1}{2}\) 。
随机取其中 \(k\) 个集合,请你帮忙算一下其中存在两个集合完全相同的概率。
- 
输入格式
一行两个整数 \(N,k\) 。
- 
输出格式
两个正整数,分别表示概率(最简分数)的分子和分母。分别对 \(10^6+3\) 取模吧。
- 
数据规模与约定
\(1 \leq N \leq 10^{18}\),\(2 \leq k \leq 10^{18}\) 。
Source: CodeForces 711E ZS and The Birthday Paradox
若 \(2^N<K\) ,答案为 \(1\) 。
否则答案为 \(\displaystyle 1- \frac{P_{2^N}^{K}}{{2^N}^K}\) 。
发现这两的 \(\gcd\) 肯定是 \(2\) 的某个次幂,并且分母中因子 \(2\) 的个数肯定比分子多。
同时约掉 \(2^N\),要在分子 \(\displaystyle\frac{P_{2^N}^{K}}{2^N} = (2^N-1)(2^N-2)\cdots(2^N-K+1)\) 中找出因子 \(2\) 的个数。
注意到 \(\left|-K+1\right|\) 中因子 \(2\) 的个数肯定比 \(2^N\) 少,所以只需要找出 \(1, 2, \cdots, K-1\) 中每个数各包含因子 \(2\) 的个数。
为 \(\displaystyle \left\lfloor\frac{K-1}{2}\right\rfloor + \left\lfloor\frac{K-1}{4}\right\rfloor + \cdots + \left\lfloor\frac{K-1}{2^i}\right\rfloor\),其中 \(i\) 为满足 \(2^i \leq K-1\) 最大的整数。
这样 \(\gcd\) 和分母就解决了,剩下分子还没算出来。
根据抽屉原理,若 \(K-1 \geq MOD\) 则连续的 \(K-1\) 个数乘积一定包含 \(MOD\) 的倍数,即分子为 \(0\) ;
否则因为 \(MOD\) 比较小,暴力算即可。
最后逆元算一下即可。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #include <cstdio> #include <cstdlib> #include <cstring> #include <climits> #define MOD 1000003 inline int _pow(register int base,register long long b) {     register int ans=1;     while(b)     {         if(b&1)             ans=(long long)ans*base%MOD;         base=(long long)base*base%MOD;         b>>=1;     }     return ans; } int main() {     long long N,K;     scanf("%lld%lld",&N,&K);     if(N<=60 && ((long long)1<<N)<K)     {         printf("%d %d\n",1,1);         return 0;     }     long long pw2=2,num=0;     for(;pw2<=K-1;pw2*=2)         num+=(K-1)/pw2;     long long ans=1;     if(K-1<MOD && (pw2=_pow(2,N)))         for(register int i=1;i<=K-1 && ans;i++)             (ans*=(pw2-i+MOD))%=MOD;     else         ans=0;     register int invgcd=_pow(_pow(2,num),MOD-2);     register long long low=(long long)_pow(_pow(2,K-1),N)*invgcd%MOD;     printf("%lld %lld\n",(low-ans*invgcd%MOD+MOD)%MOD,low);     return 0; } |