「数論~ 数論~ 数論~ 数論~ 数論大家族」
关于本题
这是一道数论大杂烩模法题,题目叙述及其冗长,真正有用的其实就只有两句话。题目压缩后可以表述为求
$\LARGE g^{\Sigma_{k|n}C^{\frac{n}{k}}_{n}}\bmod 999911659$
解答本题你需要至少会使用以下结论和定理:
本题中用到的部分还会在下文讲述。
本题中用到的结论与定理
欧拉定理推论
欧拉定理内容为:若正整数 $a,n$ 互质,则有 $a^{\varphi(n)}\equiv 1(\bmod p)$,其中 $\varphi(n)$ 为欧拉函数
我们在这道题中使用的是欧拉定理的推论:若正整数 $a,n$ 互质,则对于任意正整数 $b$ 有 $a^{b}\equiv a^{b\bmod\varphi(n)}(\bmod n)$.
可以简单地证明:设 $b=q*\varphi(n)+r,(0\le r\le \varphi(n))$,即 $r=b \bmod \varphi(n)$。于是有:
$\large a^{b} \equiv a^{q*\varphi(n)+r} \equiv (a^{\varphi(n)})^{q}a^{r} \equiv 1^qa^r \equiv a^r \equiv a^{b \bmod \varphi(n)}(\bmod n)$
其中第三步使用了欧拉定理。
中国剩余定理
设 $m_1,m_2,…,m_n$ 是两两互素的整数,$m=\Pi^{n}{i=1}m_i$,$M_i=\frac{m}{m_i}$,$t_i$ 是线性同余方程 $M_it_i \equiv 1(\bmod m_i)$ 的一个解。对于任意的 $n$ 个整数 $a_1,a_2,…,a_n$,方程组 $$ \begin{cases} x \equiv a_1(\bmod m_1)\ x \equiv a_2(\bmod m_2)\ …\ x \equiv a_n(\bmod m_n)\ \end{cases} $$ 有整数解,为 $\large x=\Sigma^n{i=1}a_iM_it_i$.
由于鄙人太菜,无法证明,所以请各位大神出门左转 OI Wiki $Q w Q$
Lucas 定理
若 $p$ 是素数,则对于任意整数 $1 \le m \le n$,有 $\large C^m_n \equiv C^{m \bmod p}{n \bmod p}*C^{m/p}{n/p}(\bmod p)$
原理是把 $n$ 和 $m$ 表示成 $p$ 进制数,对 $p$ 进制下每一位分别计算组合数,最后再乘起来。由于鄙人太菜,无法证明,所以请各位大神出门右转 OI Wiki $Q w Q$
思路
因为有 $k |n$,所以在求和时 $\frac{k}{n}$ 和 $k$ 都会被算到,原式中的 $C^{\frac{k}{n}}_{n}$ 可以变为 $C^k_n$,虽然并没有使式子变简单但是看起来更舒服。
根据欧拉定理的推论有 $\large g^{\Sigma_{k|n}C^{k}{n}}\equiv g^{\Sigma{k|n}C^{k}{n}\bmod999911658}(\bmod 999911659)$,快速地写一个简单小程序,就可以判断出模数 999911659 是一个素数,所以右边的 $\varphi(999911659)=999911658$,于是,本题的关键在于 $ \Sigma{k|n}C^{k}_{n}\bmod999911658$ 的计算。
聪敏的你一定会想到使用 Lucas 定理进行组合数取模计算,但是 Lucas 定理要求模数是一个小素数,显然我们现在的模数 999911658 并不是一个小素数——它甚至不是一个素数!还有一个 exLucas 定理仿佛可以使用,但是比较难,又会引入更多的结论和定理了。
若有正整数 $a,b$ 满足 $a \bmod b =r$ 且 $b=mn$,那么一定有 $a \bmod m=a \bmod n=r$。这是显然的——根据题意,设 $a=qb+r$,于是 $a=qmn+r$。根据这条结论,我们可以将模数 999911658 分解素因子,并使用中国剩余定理列出同余方程组。通过简单的函数可以分解模数为 $999911658=234679*35617$,于是有方程组: $$ \begin{cases} x \equiv \Sigma_{k|n}C^{k}{n}(\bmod 2)\ x \equiv \Sigma{k|n}C^{k}{n}(\bmod 3)\ x \equiv \Sigma{k|n}C^{k}{n}(\bmod 4679)\ x \equiv \Sigma{k|n}C^{k}{n}(\bmod 35617)\ \end{cases} $$ 如果可以解出 $x$,便有 $x=\Sigma{k|n}C^{k}_{n}\bmod999911658$。对于其中的每一个线性同余方程,模数都变成了一个较小的素数,我们可以利用 Lucas 定理对组合数取模。由于题目中 $n$ 的范围比较大,所以我们可以预处理每个数的阶乘以及阶乘的逆元,于是就可以更快的求组合数了。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MaxN=500005, Mod=999911659;
ll n, g, k[MaxN];
ll factorial_of[MaxN], inverseofFctorial_of[MaxN];
ll singleLeft[5], primeFactor[5], primeCount, factorCount, finalPowerTimes;
inline ll QuickPower(ll baseNumber,ll powerTimes,ll mod){
ll answer = 1;
while(powerTimes){
if(powerTimes & 1) answer = answer * baseNumber % mod;
baseNumber = baseNumber * baseNumber % mod;
powerTimes >>= 1;
}
return answer % mod;
}
inline void Init(ll mod){
factorial_of[0] = 1;
for(int i=1;i<mod;i++)
factorial_of[i] = factorial_of[i-1] * i % mod;
inverseofFctorial_of[mod] = 0;
inverseofFctorial_of[mod - 1] = QuickPower(factorial_of[mod-1], mod - 2, mod);
for(int i=mod-2;i>=0;i--)
inverseofFctorial_of[i] = inverseofFctorial_of[i+1] * (i+1) % mod;
}
inline ll C(ll x,ll y,ll mod){
if(y > x) return 0;
return factorial_of[x] * inverseofFctorial_of[y] % mod * inverseofFctorial_of[x-y] % mod;
}
inline ll Lucas(ll x,ll y,ll mod){
if(y == 0) return 1;
return Lucas(x / mod, y / mod, mod) * C(x % mod, y % mod, mod) % mod;
}
inline ll SingleLineCalculation(int x){
Init(primeFactor[x]);
for(int i=1;i<=factorCount;i++)
singleLeft[x] = (singleLeft[x] + Lucas(n, k[i], primeFactor[x])) % primeFactor[x];
}
inline ll CRT(){
ll answer = 0, mod = Mod-1;
for(int i=1;i<=primeCount;i++){
ll M = mod / primeFactor[i], t = QuickPower(M, primeFactor[i]-2, primeFactor[i]);
answer = (answer + (singleLeft[i] % mod) * (t % mod) * (M % mod)) % mod;
}
return (answer + mod) % mod;
}
inline void GetPrimeFactor(ll originalNumber){
for(int i=2;i*i<=Mod-1;i++){
if(originalNumber % i == 0){
primeFactor[++primeCount] = i;
while(originalNumber % i == 0) originalNumber /= i;
}
}
if(originalNumber != 1) primeFactor[++primeCount] = originalNumber;
}
inline void GetK(){
for(int i=1;i*i<=n;i++){
if(n % i == 0){
k[++factorCount] = i;
if(i * i != n) k[++factorCount] = n / i;
}
}
}
int main(){
scanf("%lld%lld", &n, &g);
if(g % Mod == 0){
printf("0");
return 0;
}//remember to add a special judge, or you will lose 5 pts
GetPrimeFactor(Mod - 1);
GetK();
for(int i=1;i<=primeCount;i++) SingleLineCalculation(i);
finalPowerTimes = CRT();
printf("%lld", QuickPower(g, finalPowerTimes, Mod));
return 0;
}