求 , 为质数。
设
有
也就是
考虑
由于 ,因此
设 , ,有
由二项式定理得
又 ,因此
所以
由二项式定理得
由于 在 ① 式中的系数为 ,在 ② 式中的系数为 ,因此
即
求 ,不保证 为质数。
设 ,有
考虑如何计算 ,以 为例,有
而 ,因此
设 表示 中有多少个因数 ,有递推式
如 ,其中 和 由 中的 和 乘 转移得到。
递归求解 ,最后中国剩余定理合并即可。
总复杂度 。
至于 的数组究竟开多大,你会发现将 以内的模数分解成 的话, 最大是 。
// luogu-judger-enable-o2
#include <cstdio>
#include <cmath>
typedef long long LL;
const LL N = 1000005;
LL c[N], mo[N], cnt;
LL pow(LL a, LL b, LL p) {
LL res = 1;
for (; b; b >>= 1, a = a * a % p)
if (b & 1) res = res * a % p;
return res;
}
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (b == 0) { x = 1, y = 0; return a; }
LL t = exgcd(b, a % b, y, x); y -= a / b * x; return t;
}
LL inv(LL a, LL p) {
LL x, y, t = exgcd(a, p, x, y);
return (x % p + p) % p;
}
LL fac(LL n, LL pi, LL pk) {
if (n == 0) return 1;
LL res = 1;
for (LL i = 2; i <= pk; ++i)
if (i % pi) res = res * i % pk; //1*2*4*5*7*8
res = pow(res, n / pk, pk); //(1*2*4*5*7*8)^2
for (LL i = 2; i <= n % pk; ++i)
if (i % pi) res = res * i % pk; //19
return (res * fac(n / pi, pi, pk)) % pk; //6!
}
void calc(LL n, LL m, LL pi, LL pk) {
LL up = fac(n, pi, pk), d1 = fac(n - m, pi, pk), d2 = fac(m, pi, pk);
LL k = 0; //3^6
for (LL i = n; i; i /= pi) k += i / pi;
for (LL i = m; i; i /= pi) k -= i / pi;
for (LL i = n - m; i; i /= pi) k -= i / pi;
c[++cnt] = pow(pi, k, pk) % pk * up % pk * inv(d1, pk) % pk * inv(d2, pk) % pk;
mo[cnt] = pk;
}
LL mul(LL a, LL b, LL p) {
LL res = 0, f = 1;
if (a < 0) f = -f, a = -a;
if (b < 0) f = -f, b = -b;
for (; b; b >>= 1, a = (a + a) % p)
if (b & 1) res = (res + a) % p;
return res * f;
}
LL excrt() {
LL x, y, t, mod;
for (LL i = 2; i <= cnt; ++i) {
t = exgcd(mo[1], mo[i], x, y);
if ((c[i] - c[1]) % t) return -1;
mod = mo[1] / t * mo[i];
x = mul(x, (c[i] - c[1]) / t, mod);
c[1] = (c[1] + mul(x, mo[1], mod)) % mod;
mo[1] = mod;
}
return (c[1] + mo[1]) % mo[1];
}
LL exlucas(LL n, LL m, LL p) {
LL lim = sqrt(p), tmp = p, pk;
for (LL i = 2; i <= lim; ++i) {
if (tmp % i == 0) {
pk = 1;
while (tmp % i == 0) pk *= i, tmp /= i;
calc(n, m, i, pk);
}
}
if (tmp > 1) calc(n, m, tmp, tmp);
return excrt() % p;
}
int main() {
LL n, m, p;
scanf("%lld%lld%lld", &n, &m, &p);
printf("%lld", exlucas(n, m, p));
return 0;
}