难题,这题要用到很多数论知识但都不深,有专门的知识点我就用红色标出。
(PS.洛谷上SDOI2010跟Pig有关的好多黑题,深表同情...)
首先搬出问题:
由扩展欧拉定理,
而根据欧拉函数的定义,当p为质数时,
所以问题变形:
最外一层可以用快速幂,那么现在关键就在于求解:
我们对999911658进行质因数分解,发现999911658=2*3*4679*35617。根据取模运算的性质,这一式子可以化为:
于是我们可以枚举n的因数d,这只需要根号n的时间,每枚举到一个d就分别计算对四个数取模的结果,结果对应地加在a[1~4]上。而由于这四个数又各自是质数,所以可以应用卢卡斯定理,式子可化为:
尽管已经化简了很多,但是每次组合数重新计算阶乘耗时巨大,因此需要预处理出35617以内的每个数的阶乘对999911658取模结果(打个比方,20既然能被10整除,也必然能被10的因数整除)。然后在计算组合数时单独求m!和(n-m)!的逆元即可。
得到对上面四个数取模的结果后,因为我们最终要找一个数能代替
所以我们需要求出一个最小的x,满足:
由于所有的模数互质,因此这是一个简单的中国剩余定理,最后的x对999911658取模,并保留非负整数解。
整个过程中,逆元和中国剩余定理用扩展欧几里得算法解决。
最后既然最棘手的解决了,剩下的就是快速幂了。记得此时的取模对象为999911659。
注意坑点:
1,中间过程long long。
2,当G=999911659,输出0即可。
3,最后快速幂时取模对象是999911659,之前的都是999911658。
#include<cstdio>
using namespace std;
typedef long long LL;
const int MAXN=35617;
int N,G,fact[MAXN+5],mod=999911658;
int prime[5]={0,2,3,4679,35617},Mod[5];
void get_fact()
{
fact[0]=1;
for(int i=1;i<=MAXN;i++)
fact[i]=(LL)fact[i-1]*i%mod;
}
int ex_t;
void exgcd(int a,int b,int &x,int &y)
{
if(!b) {x=1;y=0;return;}
exgcd(b,a%b,x,y);
ex_t=x; x=y; y=ex_t-(a/b)*y;
}
int inv(int a,int p)
{
int x,y;
exgcd(a,p,x,y);
return (x%p+p)%p;
}
int calc(int i,int p)
{
int ret=1,x,y,n,m;
for(x=N,y=i;y;x/=p,y/=p)
{
n=x%p; m=y%p; //卢卡斯定理+预处理阶乘+逆元
ret=(LL)ret*fact[n]%p*inv(fact[m],p)%p*inv(n<m?0:fact[n-m],p)%p;
}
return ret;
}
LL qkpow(int x,int b)
{
int s=1;
while(b)
{
if(b&1) s=(LL)s*x%mod;
x=(LL)x*x%mod;
b>>=1;
}
return s;
}
int main()
{
scanf("%d%d",&N,&G);
if(G%(mod+1)==0) //坑点
{
printf("0");
return 0;
}
get_fact(); //得到阶乘取模结果
for(int i=1;i*i<=N;i++) //枚举因数
{
if(N%i==0)
{
for(int j=1;j<=4;j++) Mod[j]=(Mod[j]+calc(i,prime[j]))%prime[j];
if(i*i!=N)
for(int j=1;j<=4;j++) Mod[j]=(Mod[j]+calc(N/i,prime[j]))%prime[j];
}
}
int x,y,b=0;
for(int i=1;i<=4;i++) //中国剩余定理
{
exgcd(mod/prime[i],prime[i],x,y);
b=(b+(LL)Mod[i]%mod*(mod/prime[i])%mod*x%mod)%mod;
}
b=(b%mod+mod)%mod; mod+=1; //记得mod+1
printf("%lld",qkpow(G,b));
return 0;
}