zoukankan      html  css  js  c++  java
  • [BZOJ 1951] 古代猪文

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

    Solution:

    见过最长的数论题题面.......

    一道数论的综合题,求解:

    [g^{sum_{d|n} C_n^d}~mod~p]

    我们先看幂能否化简,

    由费马小定理可知

    egin{aligned} displaystyle ans &=  g^{sum_{d|n} C_n^d}~mod~p  \  &=g^{sum_{d|n} C_n^d~mod~(p-1)}~mod~p  end{aligned}

    对于化简后的幂:

    [sum_{d|n} C_n^d~mod~(p-1)]

    我们想到枚举每一个n的约数,使用Lucas定理求大组合数取模

    但P-1=2*3*4679*35617,不是一个质数

    于是我们以每一个质因子为模数算出最终的解

    由于质因子之间是互质的,再用中华剩余定理合并结果算出最小的[sum_{d|n} C_n^d],即为[sum_{d|n} C_n^d~mod~(p-1)]

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
    const int MAXN=1e5+10;
    const int MOD=999911659;
    
    int quick_pow(ll a,ll b,int p) //快速幂
    {
        ll ret=1;a%=p;
        while(b)
        {
            if(b&1) ret=ret*a%p;
            b>>=1;a=a*a%p; 
        }
        return ret;
    }
    
    int inv(ll a,int p){return quick_pow(a,p-2,p);}  //求逆元
    
    int g,n,fac[4][MAXN],c[4];
    int pri[]={2,3,4679,35617};
    
    int C(int n,int m,int p)  //求组合数
    {
        if(n>m) return 0;
        return 1ll*fac[p][m]*inv(1ll*fac[p][n]*fac[p][m-n],pri[p])%pri[p];
    }
    
    int Lucas(int n,int m,int p)  //Lucas定理
    {
        if(m==0) return 1;
        return C(n%pri[p],m%pri[p],p)*Lucas(n/pri[p],m/pri[p],p)%pri[p];
    }
    
    ll CRT()  //中国剩余定理
    {
        ll ret=0,p=MOD-1;
        for(int i=0;i<4;i++) 
            ret=(ret+1ll*p/pri[i]*inv(p/pri[i],pri[i])*c[i])%p;
        return ret%MOD;
    }
    
    int main()
    {
        cin >> n >> g;
        if(g==MOD) return cout << 0,0;
        for(int i=0;i<4;i++)  //预处理阶乘
        {
            fac[i][0]=1;
            for(int j=1;j<=pri[i];j++)
                fac[i][j]=fac[i][j-1]*j%pri[i];
        }
        
        for(int i=0;i<4;i++)
            for(int j=1;j*j<=n;j++)
                if(n%j==0)
                {
                    c[i]=(c[i]+Lucas(j,n,i))%pri[i];
                    if(j*j!=n) c[i]=(c[i]+Lucas(n/j,n,i))%pri[i];
                }
        
        cout << quick_pow(g,CRT(),MOD);
        return 0;
    }

    Review:

    1、如果模数为质数,

    利用费马小定理对幂化简

    2、遇到大组合数取模  ------->   Lucas定理

    如果模数不为质数,质因数分解后分别求出结果再用中华剩余定理合并,套路啊……

    3、中华剩余定理求解方法

    (1)递推式地两两求解

    (2)根据推导出的结论直接求解:

    [x=(sumlimits_{i=1}^k c_i*{Mover m_i}*inv({Mover m_i},mi))\%M]

  • 相关阅读:
    转载:通过Servlet生成验证码
    转载:web工程中URL地址的推荐写法
    使用Git上传本地项目代码到github
    $watch 和 $apply
    平时用的sublime插件
    zTree.js
    js
    npm install详解
    git
    git基础
  • 原文地址:https://www.cnblogs.com/newera/p/9088919.html
Copyright © 2011-2022 走看看