问题:http://codeforces.com/contest/711/problem/E
概率计算问题,需结合数论知识化简运算规模
关键知识点:
1. gcd(a, b) = gcd(b - a, b or a) (if b > a)
2. x | 2^i ===> 2^n - x | 2^i (n >= i) 从而将求能整除 2^n - x的最大2次幂转换为求能整除x的最大二次幂
3. Legendre's formula 求能整除k!的最大二次幂,时间复杂度O(log k)
4. 取模运算相关的性质
i) 取模对加、减、乘是封闭的
ii)若a能整除b, 则 (a / b) % p = (a % p + k * p) / (b % p), 其中 k < p - 1
证明:
记 a = b * c, 其中 b = m * p + y1, c = n * p + y2
则有
(a / b)% p = c % p = y2
另一方面
a % p = (b * c) % p = (y1 * y2) % p, b % p = y1
由于
0 <= y1 * y2 <= (p - 1)^2 ===> y1 * y2 - a % p = k * p, 0 <= k < p - 1
要使命题成立,则需令
a % p + k * p = y1 * y2, 且 0 <= k < p - 1
iii)费马小定理 应用:简化取模计算
p为质数,则有 a^p ≡ a (mod p);若gcd(a, p) = 1, 进一步有a^(p - 1) ≡ 1 (mod p)
从而有
a^x ≡ a^(x % (p-1)) (mod p) 即将 a^x 中所有 a^(p-1) 去除
5. 在求分子项时涉及到除法运算,亦可使用费马小定理求逆元
概率计算问题,需结合数论知识化简运算规模
关键知识点:
1. gcd(a, b) = gcd(b - a, b or a) (if b > a)
2. x | 2^i ===> 2^n - x | 2^i (n >= i) 从而将求能整除 2^n - x的最大2次幂转换为求能整除x的最大二次幂
3. Legendre's formula 求能整除k!的最大二次幂,时间复杂度O(log k)
https://en.wikipedia.org/wiki/Legendre%27s_formula
4. 取模运算相关的性质
i) 取模对加、减、乘是封闭的
ii)若a能整除b, 则 (a / b) % p = (a % p + k * p) / (b % p), 其中 k < p - 1
证明:
记 a = b * c, 其中 b = m * p + y1, c = n * p + y2
则有
(a / b)% p = c % p = y2
另一方面
a % p = (b * c) % p = (y1 * y2) % p, b % p = y1
由于
0 <= y1 * y2 <= (p - 1)^2 ===> y1 * y2 - a % p = k * p, 0 <= k < p - 1
要使命题成立,则需令
a % p + k * p = y1 * y2, 且 0 <= k < p - 1
iii)费马小定理 应用:简化取模计算
p为质数,则有 a^p ≡ a (mod p);若gcd(a, p) = 1, 进一步有a^(p - 1) ≡ 1 (mod p)
从而有
a^x ≡ a^(x % (p-1)) (mod p) 即将 a^x 中所有 a^(p-1) 去除
5. 在求分子项时涉及到除法运算,亦可使用费马小定理求逆元
http://blog.csdn.net/ac__y/article/details/9248023
实现:
#include
using namespace std;
typedef long long ll;
const int MOD = int(1e6 + 3);
ll n, k;
ll powM(ll a, ll b)
{
ll rsl = 1;
while (b)
{
if (b & 1) rsl = rsl * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return rsl;
}
ll sum()
{
ll rsl, base;
rsl = 0, base = 2;
while (base <= k - 1)
{
rsl += (k - 1) / base;
base <<= 1;
}
return rsl;
}
void solve()
{
cin >> n >> k;
if (n <= 62 && k > (1LL << n))
{
cout << 1 << " " << 1 << endl;
return;
}
ll up, down, gcd = powM(2, sum());
//down = powM(2, ((n % (MOD - 1)) * ((k - 1) % (MOD - 1)) - sum() % (MOD - 1)) % (MOD - 1));
down = powM(2, (n % (MOD - 1)) * ((k - 1) % (MOD - 1)) % (MOD - 1));
while (down % gcd)
down += MOD;
down /= gcd;
if (k - 1 >= MOD)
up = 0;
else
{
ll tmp = 1, base = powM(2, n);
for (ll i = 1; i < k; ++i)
tmp = (tmp * (base - i)) % MOD;
while (tmp % gcd)
tmp += MOD;
up = tmp / gcd;
}
if (down < up)
up -= MOD;
cout << down - up << " " << down << endl;
}
int main()
{
solve();
return 0;
}