Codeforces 711E - ZS and The Birthday Paradox
Published on 2016-08-30描述
某个星球有一年 天,现在抽取 个人,问你至少有两个人生日为同一天的概率是多少?假设答案为 ,你需要输出的是 。
分析
如果 ,根据鸽笼原理,必定存在两个人的生日处于同一天,直接输出 1 1
。
下面假设 。
直接算至少两个人生日相同的概率并不好算,我们算没有两个人生日相同的概率。首先固定第一个人的生日,则第二个人有 种选择,第三个人有 种选择,可得没有两个人生日相同的概率:
所以至少有两个人生日相同的概率为:
假设 ,则 ,题目要求先化为最简形式(互质)再取模,根据 ,我们只需要求出 的最简形式取模即可,这是因为:
由于 ,所以我们可以先算 的最简形式取模,再用 1 来减,否则是不能够取模了之后再用 1 来减的。
我们现在求 ,观察发现 一定是 的幂,而且分母中的 明显比分子中的 要多,我们考虑求分子中有多少个 ,即能整除分子的最大的 。我们的思路是求出分子分母的模,然后乘上 的逆元。
我们发现这样一个事实,如果有最大的 使得 能整除 ,那么它也是最大的 使得 能整除 ,反过来也成立。证明十分简单,对于 来说,最大的 就是 ,而使用 来减 的话,必定有
现在我们就是要求 中有多少个 以得到 ,根据 Legendre's formula,可以在 的时间内求出,而使用 __builtin_popcountll()
的话更是可以除一个很大的常数。
接下来我们要算分子,如果 ,分子必然为 ,因为必然存在模的倍数。只剩下 的情况,这样分子就可以在至多 的时间内算出。
接着算分母 ,发现 会溢出,费马小定理来帮忙,只需要算 的值就行了。
剩下的工作就是乘逆元,然后用 1 来减就行了。
P.S.:真是个数论综合知识运用的好题啊,我考试就是卡在转换到 这一步了。
代码
// Created by Sengxian on 08/30/16. // Copyright (c) 2016年 Sengxian. All rights reserved. // Codeforces 711E 数论 #include <bits/stdc++.h> using namespace std; typedef long long ll; const int modu = 1000003; ll n, k; inline ll mod_mul(ll a, ll b, int modu = ::modu) { ll res = 0; while (b) { if (b & 1) (res += a) %= modu; (a <<= 1) %= modu; b >>= 1; } return res; } inline ll mod_pow(ll a, ll b) { ll res = 1; while (b) { if (b & 1) (res *= a) %= modu; (a *= a) %= modu; b >>= 1; } return res; } int main() { scanf("%lld%lld", &n, &k); if (n <= 62 && k > (1LL << n)) return puts("1 1\n"), 0; ll num = 1, deno = 0, pow = 0, inv; // cal gcd (2 ^ pow) pow = k - 1 - __builtin_popcountll(k - 1); // cal numerator if ((k - 1) < modu) { ll tmp = (mod_pow(2, n) + modu - 1) % modu; for (int i = 0; i < k - 1; ++i) { (num *= tmp) %= modu; (tmp += modu - 1) %= modu; } } else num = 0; // cal denominator 2 ^ n(k - 1) deno = mod_pow(2, mod_mul(n, k - 1, modu - 1)); // cal inv of gcd inv = mod_pow(mod_pow(2, pow), modu - 2); (num *= inv) %= modu, (deno *= inv) %= modu; printf("%lld %lld\n", (deno - num + modu) % modu, deno); return 0; }