[BZOJ 1951] 古代猪文
Link:https://www.lydsy.com/JudgeOnline/problem.php?id=1951
Solution:
见过最长的数论题题面.......
一道数论的综合题,求解:
\[g^{\sum_{d|n} C_n^d}~mod~p\]
我们先看幂能否化简,
由费马小定理可知:
\begin{aligned} \displaystyle ans &= g^{\sum_{d|n} C_n^d}~mod~p \\ &=g^{\sum_{d|n} C_n^d~mod~(p-1)}~mod~p \end{aligned}
对于化简后的幂:
\[\sum_{d|n} C_n^d~mod~(p-1)\]
我们想到枚举每一个n的约数,使用Lucas定理求大组合数取模
但P-1=2*3*4679*35617,不是一个质数
于是我们以每一个质因子为模数算出最终的解
由于质因子之间是互质的,再用中华剩余定理合并结果算出最小的\[\sum_{d|n} C_n^d\],即为\[\sum_{d|n} C_n^d~mod~(p-1)\]
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=1e5+10;
const int MOD=999911659;
int quick_pow(ll a,ll b,int p) //快速幂
{
ll ret=1;a%=p;
while(b)
{
if(b&1) ret=ret*a%p;
b>>=1;a=a*a%p;
}
return ret;
}
int inv(ll a,int p){return quick_pow(a,p-2,p);} //求逆元
int g,n,fac[4][MAXN],c[4];
int pri[]={2,3,4679,35617};
int C(int n,int m,int p) //求组合数
{
if(n>m) return 0;
return 1ll*fac[p][m]*inv(1ll*fac[p][n]*fac[p][m-n],pri[p])%pri[p];
}
int Lucas(int n,int m,int p) //Lucas定理
{
if(m==0) return 1;
return C(n%pri[p],m%pri[p],p)*Lucas(n/pri[p],m/pri[p],p)%pri[p];
}
ll CRT() //中国剩余定理
{
ll ret=0,p=MOD-1;
for(int i=0;i<4;i++)
ret=(ret+1ll*p/pri[i]*inv(p/pri[i],pri[i])*c[i])%p;
return ret%MOD;
}
int main()
{
cin >> n >> g;
if(g==MOD) return cout << 0,0;
for(int i=0;i<4;i++) //预处理阶乘
{
fac[i][0]=1;
for(int j=1;j<=pri[i];j++)
fac[i][j]=fac[i][j-1]*j%pri[i];
}
for(int i=0;i<4;i++)
for(int j=1;j*j<=n;j++)
if(n%j==0)
{
c[i]=(c[i]+Lucas(j,n,i))%pri[i];
if(j*j!=n) c[i]=(c[i]+Lucas(n/j,n,i))%pri[i];
}
cout << quick_pow(g,CRT(),MOD);
return 0;
}
Review:
1、如果模数为质数,
利用费马小定理对幂化简
2、遇到大组合数取模 -------> Lucas定理
如果模数不为质数,质因数分解后分别求出结果再用中华剩余定理合并,套路啊……
3、中华剩余定理求解方法
(1)递推式地两两求解
(2)根据推导出的结论直接求解:
\[x=(\sum\limits_{i=1}^k c_i*{M\over m_i}*inv({M\over m_i},mi))\%M\]