bzoj1951 [Sdoi2010]古代猪文 ——数论综合

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1951

题意就是要求 G^( ∑(k|n) C(n,k) ) % p,用费马小定理处理指数,卢卡斯定理处理大组合数,取模用中国剩余定理合并;

好想难写的感觉(其实也不难写?);

关于中国剩余定理,可以看这篇博客:https://www.cnblogs.com/MashiroSky/p/5918158.html

第一次写中国剩余定理合并模数,还有一点好不容易理解的地方,写在注释里。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
ll n,g,p=999911659,t[4]={2,3,4679,35617},r[5],fac[5][40005];
ll pw(ll a,int b,int mod)
{
//    a%=mod;//可有可无 
    ll ret=1;
    for(;b;b>>=1,(a*=a)%=mod)
        if(b&1) (ret*=a)%=mod;
    return ret;
}
ll C(int n,int m,int k)
{
    if(n<m)return 0;
    return (fac[k][n]*pw(fac[k][m]*fac[k][n-m],t[k]-2,t[k]))%t[k];
}
ll Lucas(ll n,ll m,int k)
{
    if(m==0)return 1;//!
    return (C(n%t[k],m%t[k],k)*Lucas(n/t[k],m/t[k],k))%t[k];
}
void exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0){x=1; y=0; return;}
    exgcd(b,a%b,x,y);
    ll t=x; x=y; y=t-a/b*y;
}
int CRT()
{
    ll M=t[0],R=r[0],x,y;
    for(int i=1;i<4;i++)
    {
        exgcd(M,t[i],x,y);
        x=((r[i]-R)%t[i]*x%t[i]+t[i])%t[i];//%t[i]!(CRT取通解方法)  //不能是R-r[i]!因为求到的x对于r[i]-R来说是正的,否则需要取一下负 
        R+=M*x;
        M*=t[i];
    }
    return R;
}
int main()
{
    scanf("%lld%lld",&n,&g);
    if(g==p)//!!!
    {
        printf("0"); return 0;
    }
    for(int i=0;i<4;i++)
    {
        fac[i][0]=1;
        for(int j=1;j<=t[i];j++)
            fac[i][j]=(fac[i][j-1]*j)%t[i];
    }
    for(int j=0;j<4;j++)
        for(int i=1;i*i<=n;i++)//1
            if(n%i==0)
            {
                (r[j]+=Lucas(n,i,j))%=t[j];
                if(i*i!=n) (r[j]+=Lucas(n,n/i,j))%=t[j];
            }
    int ans=CRT();
    printf("%lld",pw(g,ans,p));
    return 0;
}

 

posted @ 2018-07-03 17:24  Zinn  阅读(195)  评论(0编辑  收藏  举报