传送门
写在前面:写的最艰辛的一道数论
思路:
1.对于a^x mod p(其中p是质数),由x=0,1,2..p-1,一定可以构成循环节,所以我们可以把它变为a^(x mod (p-1)),求出x mod (p-1)使用快速幂
2.对于p-1=999911659=2×3×4679×35617,所以我们可以将x mod (p-1)分解成一元线性同余方程组,最后使用中国剩余定理合并。
3.对于x=C(i,n)(i是n的约数),p为质数(注意,这里的p为第二步分解出的质因数),我们可以使用Lucas定理有C(i,n)mod p=C(i/p,n/p)×C(i%p,n%p) mod p,对于C(i/p,n/p)可以递归至其中一个为0,C(i%p,n%p)则可以通过预处理直接得出n!/(n-i)!/i!,但注意的是由于取模p,我们对于C(i%p,n%p)计算时应当是n!×inv((n-i)!*i!),inv是关于p的乘法逆元,对于这个逆元我们可以用费马小定理求出,即为((n-i)!×i!)^-2
4.关于一些具体求法,详见代码
代码:
#include<bits/stdc++.h>
#define LL long long
#define mod 999911659
using namespace std;
LL n,g,e;
LL prime[5]={0,2,3,4679,35617},fac[35618]={1},ans[5],M[5]={0,499955829,333303886,213702,28074};
//prime为分解的质因数,fac为阶乘,ans为对于每一个质因数得出的sigma(C(i,n)),M[i]为除了ni以外的n-1个整数的乘积。
LL qr(LL x,LL y,LL z)
{
LL ans=1;
x%=z;
while (y)
{
if (y&1) ans=ans*x%z;
x=x*x%z;
y>>=1;
}
return ans;
}
LL lucas(LL p,LL q,LL num)
{
LL sum=1;
while (p&&q)
{
LL x=p%prime[num],y=q%prime[num];
if (y>x) return 0;
sum=(sum*(fac[x]*qr(fac[y]*fac[x-y],prime[num]-2,prime[num]))%prime[num])%prime[num];
p/=prime[num];
q/=prime[num];
}
return sum;
}
void exgcd(LL &x,LL &y,LL a,LL b)
{
if (!b) {x=1;y=0;return;}
exgcd(x,y,b,a%b);
int x1=y,y1=x-a/b*y;
x=x1;
y=y1;
}
main()
{
scanf("%lld%lld",&n,&g);
for (LL i=1;i<=4;i++)
{
for (LL j=1;j<=prime[i];j++)
fac[j]=fac[j-1]*j%prime[i];
//预处理阶乘
for (LL j=1;j<=sqrt(n);j++)
if (n%j==0)
{
ans[i]=(ans[i]+lucas(n,j,i))%prime[i];
if (j*j<n) ans[i]=(ans[i]+lucas(n,n/j,i))%prime[i];
}
}
//通过lucas求出组合数累加和,构建同余方程组
for(LL i=1;i<=4;i++)
{
LL x,y;
exgcd(x,y,M[i],prime[i]);//求出逆元,即ti
e=(e+ans[i]*x*M[i])%(mod-1);
}
while (e<=0) e+=(mod-1);//这步很关键,而且不能是<,必须是<=.因为g是mod的倍数的时候,g^x%mod一定是0,但是当e=0就不加的话,就是g^0%mod=1了
printf("%lld\n",qr(g,e,mod));
}