【codevs1830】【BZOJ1951】古代猪文,数论综合

传送门
写在前面:写的最艰辛的一道数论
思路:
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=1printf("%lld\n",qr(g,e,mod));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值