求:
其中 不保证为质数。
前置知识
中国剩余定理(CRT)
ExGCD求逆元
考虑令
原问题转化为求:
的一个解,使用中国剩余定理合并即可。
针对其中一个式子 即 ,使用求逆元的方法是不行的,因为 不保证为质数, 可能不存在逆元。
设 ,进一步考虑,既然不互质,那就把他们的公因子都约掉,我们把阶乘展开看一看:
其中 是指最后不够一整段 的剩下的项, 是在 意义下的一个循环节,这样的循环节有 个。
每这样一次,我们都可以从阶乘提取出 这个因子,并把原问题变为一个规模更小的问题 ,可以递归进行,并在过程中累加 ,记为 ,最后的 就是 中 这个因子的数量。
至此,我们成功把 里的 的因子分离出来。回归原来的问题 :
即
由于 已经是被约掉 因子的,一定有逆元,于是CRT的一个式子就解决了,最后合并就好了。
Talk is cheap, show me your code
#include <cstdio>
typedef long long ll;
const int N = 100000;
int p, cnt, pri[N], k[N], cpoy_p;
ll n, m;
inline ll qpow(ll base, ll exp, const int &mod) {
ll res = 1;
while (exp) {
if (exp & 1) res = res * base % mod;
base = base * base % mod;
exp >>= 1;
}
return res;
}
void exgcd(ll a, ll b, ll &x, ll &y) {
if (b == 0) return x = 1, y = 0, void();
exgcd(b, a % b, y, x);
y -= a / b * x;
}
namespace CRT {
int a[N], ans = 0, pk;
ll inv, y;
inline int solve() {
for (int i = 1; i <= cnt; ++i) {
pk = qpow(pri[i], k[i], p + 1);
exgcd(p / pk, pk, inv, y);
ans += a[i] * inv % p * (p / pk) % p;
if (ans >= p) ans -= p;
if (ans < 0) ans += p;
}
return ans;
}
} // namespace CRT
namespace Exlucas {
int prime, mod; // mod = p^k
ll h; //指数也要开long long
inline void MOD(ll &x, const int &mod) {
if (x >= mod) x %= mod;
}
ll f(ll n) {
if (n == 0) return 1;
h += n / prime;
ll res = 1, tmp = 1;
res *= f(n / prime);
MOD(res, mod);
for (int i = 1; i <= mod; ++i) {
if (i % prime) (tmp *= i) %= mod;
}
res *= qpow(tmp, n / mod, mod);
MOD(res, mod);
for (ll i = n / mod * mod; i <= n; ++i) {
if (i % prime) {
res *= i % mod;
MOD(res, mod);
}
}
return res;
}
inline ll solve(int i) {
prime = pri[i], mod = qpow(prime, k[i], p + 1);
int exp = 0;
ll res = 1, inv, y;
h = 0, res *= f(n), exp += h;
h = 0, exgcd(f(m), mod, inv, y);
inv = (inv % mod + mod) % mod;
res *= inv;
MOD(res, mod);
exp -= h;
h = 0, exgcd(f(n - m), mod, inv, y);
inv = (inv % mod + mod) % mod;
res *= inv;
MOD(res, mod);
exp -= h;
return res * qpow(prime, exp, mod) % mod;
}
} // namespace Exlucas
signed main() {
#ifndef ONLINE_JUDGE
#ifdef LOCAL
freopen("testdata.in", "r", stdin);
freopen("testdata.out", "w", stdout);
#else
freopen("ExLucas.in", "r", stdin);
freopen("ExLucas.out", "w", stdout);
#endif
#endif
scanf("%lld%lld%d", &n, &m, &p);
cpoy_p = p;
for (int i = 2; i * i <= p; ++i) {
if (cpoy_p % i == 0) {
pri[++cnt] = i;
while (cpoy_p % i == 0) { cpoy_p /= i, k[cnt]++; }
}
}
if (cpoy_p != 1) pri[++cnt] = cpoy_p, k[cnt] = 1;
for (int i = 1; i <= cnt; ++i) { CRT::a[i] = Exlucas::solve(i); }
printf("%d", CRT::solve());
return 0;
}