首页 > 代码库 > 【BZOJ】1951[Sdoi2010]古代猪文

【BZOJ】1951[Sdoi2010]古代猪文

【算法】欧拉定理+组合数取模(lucas)+中国剩余定理(CRT)

【题解】给定G,N

技术分享

先考虑简化幂运算,因为模数为素数,由欧拉定理可知G^k=G^(k%φ(p)) mod p,显然G^(k%φ(p)) mod p可以用快速幂求解

此时观察到2p>max(n)>p,所以可能n=p,此时不满足n,p互素,答案直接为0。

对于幂取模部分,因为p是素数,φ(p)=p-1,p-1=999911658=2*3*4679*35617。

由于lucas定理要求p为素数,所以用套路:分开求解后用中国剩余定理合并。

因为p-1分解后无平方因子,所以运算十分方便,若有则需要参考bzoj礼物的方法。

过程中出现的逆元运算均可以用欧拉定理的方法简单实现,因为模数是素数。

最后用中国剩余定理合并即可。

技术分享
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=100010,MOD=999911659;//999911658=2*3*4679*35617
const int p[5]={0,2,3,4679,35617};
ll a[5],fac[5][maxn],n,G;
ll power(ll x,ll k,ll p)
{
    if(x==0)return 0;
    ll ans=1;//快速幂ans=1! 
    while(k>0)
    {
        if(k&1)ans=(ans*x)%p;//满足1时才累乘 
        x=(x*x)%p;
        k>>=1;
    }
    return ans;
}
ll C(ll n,ll m,ll k)
{
    if(n<m)return 0;
    return fac[k][n]*power(fac[k][m],p[k]-2,p[k])%p[k]*power(fac[k][n-m],p[k]-2,p[k])%p[k];//n!/m!/(n-m)!
}    
ll lucas(ll n,ll m,ll k)
{
    if(n<m)return 0;
    if(n<p[k]&&m<p[k])return C(n,m,k);
    return C(n%p[k],m%p[k],k)*lucas(n/p[k],m/p[k],k)%p[k];
}
int main()
{
    scanf("%lld%lld",&n,&G);
    if(G==MOD)
    {
        printf("0");
        return 0;
    }
    for(int k=1;k<=4;k++)
    {
        fac[k][0]=1;
        for(int i=1;i<p[k];i++)fac[k][i]=fac[k][i-1]*i%p[k];//随时记得取模 
    }
    for(int i=1;i*i<=n;i++)if(n%i==0)
    {
        int j=n/i;
        for(int k=1;k<=4;k++)
        {
            a[k]=(a[k]+lucas(n,i,k))%p[k];
            if(i!=j)a[k]=(a[k]+lucas(n,j,k))%p[k];
        }
    }
    ll M=MOD-1;
    ll ans=0;
    for(int k=1;k<=4;k++)ans=(ans+a[k]*M/p[k]*power(M/p[k],p[k]-2,p[k]))%M;
    printf("%lld",power(G,ans,MOD));
    return 0;
}
View Code

 

【BZOJ】1951[Sdoi2010]古代猪文