题目链接
题意:设一年有2^n个日子,现在有k个人,求至少有两个人在同一天的概率是多少,写成A/B(A,B互质)这样的格式,之后再对1e6+3取模。
思路:很简单的就能想到,只要求所有人的生日都不在同一天的概率,拿1减就行了。如果2^n<k,那么必然有两个人在同一天,输出“1 1”即可。
如果2^n>=k,那么分子为k!c(2^n,k),这是所有人不在同一天的情况。分母为2^(kn),这是总的情况。
化简一下分子是∏(2^n-i),i=1,2…k-1、
分母是2^(k-1)n
如果仅仅是这样的话,这题就是个大水题了。
但是n,k都巨大,要取模,所以这题就变得十分困难。
首先上下要同除以公因式。观察一下,分母是2的幂次,所以公因式一定是2的幂次。看分子,2^n-i,当且仅当i是2的幂次时,这个式子才对公因式有贡献。所以一个简单的想法是把所有的i都算一遍加在一起记为cnt,上下同乘cnt对于mod的逆元就行了,正好,当k-1>=mod的时候,分子一定为零,直接输出1 1就行,k-1<mod的时候可以暴力求分子,是不是感觉很美妙?
然而事实证明这是个sb想法……第四个数据就wa了……
错在了顺序,题目要求先除以公因式,再取模,而上面的做法是取模之后再除以公因式。
那么,怎么样求1,2……k-1中2的幂次乘积呢?、
题解提供了一个叫勒让德定理的东西
链接在这勒让德定理
虽然不知道具体怎么证的,反正就开心的用啦,用完就神奇的ac啦。
好吧,完全没有上进心。
代码
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| #include<stdio.h> #include<string.h> #include<algorithm> #include<math.h> using namespace std; long long mod=1000000+3; long long pp(long long a,long long b) { long long ans=1; a=a%mod; while(b>0) { if(b%2==1) ans=ans*a%mod; b=b/2; a=a*a%mod; } return ans%mod; } int main() { long long i,j,k,m,n,a,b; scanf("%lld%lld",&n,&k); long long temp=1; int flag=0; for(int i=1;i<=n;i++) { temp*=2; if(temp>=k) { flag=1; break; } } if(flag==0) { printf("1 1\n"); return 0; } temp=0; if(k-1>=mod) { long long temp=0; long long kk=k-1; long long p=2; while(p<=kk) { temp=(temp+(k-1)/p); p*=2; } long long b=pp(2,n); temp=pp(2,temp)%mod; b=pp(b,k-1)%mod*pp(temp,mod-2)%mod; printf("%lld %lld\n",b,b); return 0; } temp=0; long long kk=k-1; long long p=2; while(p<=kk) { temp=(temp+(k-1)/p); p*=2; } long long aa=pp(2,n); a=1; b=1; for(int i=1;i<=k-1;i++) { a=a*(aa-i)%mod; } long long tt=pp(2,temp); a=a*pp(tt,mod-2)%mod; b=pp(2,n); b=pp(b,k-1); b=b*pp(tt,mod-2)%mod; a=b-a; a=(a+mod)%mod; printf("%lld %lld\n",a,b); return 0; }
|