Codeforces - 711E
生日攻击的模型,现在有 2^n天,k个人,问不冲突的概率
其中 n和 k都是在 LL范围内的数,并且要输出既约分数
很容易就列出计算式,难点就在于约分
算式大概是
1−ab=b−ab
这个形式的
然后可得
gcd(b−a,b)=gcd(a,b)
所以只要算
gcd(a,b)
即可
a=(2n−1)(2n−2)...(2n−(k−1))
b=2n(k−1)
然后发现分母是2的幂,所以 gcd 也是 2的幂
只要求 a最多能被 2整除几次
试着把 a中的每一项的 2次幂都提出来
发现其能被 2整除的次数与
(k−1)!
相同
而由Legendre公式,这个值可以
(logK)
算出来
接下来就没什么难点了
算分子的话,如果
k−1<MOD
,直接暴力算
否则由于鸽笼原理,其值取模为 0
(连续的数相乘,个数超过MOD个,必然有 MOD的倍数,不仅阶乘如此)
算分母的话,用费马小定理降幂,然后再快速幂算
注意一下取模的结果最好弄成正的,特别在调用快速幂或者输出的时候
#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <map>
#include <set>
#include <queue>
#include <bitset>
#include <string>
#include <complex>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define SQR(a) ((a)*(a))
#define PCUT puts("\n----------")
const LL MOD=1e6+3;
LL N,K;
LL Pow(LL,LL,LL);
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
#endif
while(~scanf("%lld%lld", &N, &K))
{
if(N<=63 && 1LL<<N < K)
{
printf("1 1\n");
continue;
}
LL P,Q,D;
Q = (N%(MOD-1))*(K%(MOD-1)-1)%(MOD-1);
if(Q<0) Q+=MOD-1;
Q = Pow(2, Q, MOD);
if(K-1>=MOD) P=0;
else
{
LL v2pow = Pow(2, N, MOD);
P = 1;
for(int i=1; i<=K-1; i++) P = P*(v2pow-i)%MOD;
}
D=0;
for(LL tem=K-1; tem; tem>>=1) D += (tem>>1);
D = Pow(2, D, MOD);
// printf("P:%lld Q:%lld D:%lld\n", P, Q, D);
P = (Q-P)*Pow(D, MOD-2, MOD)%MOD;
Q = Q*Pow(D, MOD-2, MOD)%MOD;
if(P<0) P+=MOD;
if(Q<0) Q+=MOD;
printf("%lld %lld\n", P, Q);
}
return 0;
}
LL Pow(LL x, LL n, LL p)
{
LL res=1;
while(n)
{
if(n&1) res=res*x%p;
x=x*x%p;
n>>=1;
}
return res;
}