算法笔记--数论模板小集(待增)

Posted Wisdom+.+

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法笔记--数论模板小集(待增)相关的知识,希望对你有一定的参考价值。

gcd

一行版:

unsigned int gcd(unsigned int a,unsigned int b)
{
    return b>0?gcd(b,a%b):a; 
}

快速幂和快速乘

ll pow(ll n,ll k)
{
    ll ans=1;
    while(k)
    {
        if(k%2) ans*=n;
        n*=n;
        k/=2;
    }
    return ans;
}
ll mul(ll n,ll k)
{
    int ans=0;
    while(k)
    {
        if(k%2) ans+=n;
        n+=n;//n*=2;
        k/=2;
    }
    return ans;
}

扩展欧几里得(扩展gcd)

int e_gcd(int a,int b,int &x,int &y)
{
    if(!b)
    {
        x=1;
        y=0;
        return a;
    }
    int ans=e_gcd(b,a%b,x,y);
    int temp=x;
    x=y;
    y=temp-a/b*y;
    return ans;
}
int e_gcd(int a,int b,int &x,int &y){
    if (!b)
    {
        x=1,y=0;
        return a;
    }
    int ans=e_gcd(b,a%b,y,x);//注意x,y位置和上面不同
    y-=a/b*x;
    return ans;
}

乘法逆元

扩展欧几里得版:

ll e_gcd(ll a,ll b,ll &x,ll &y)
{
    if(!b)
    {
        x=1;
        y=0;
        return a;
    }
    ll ans=e_gcd(b,a%b,x,y);
    ll temp=x;
    x=y;
    y=temp-a/b*y;
    return ans;
}
ll inv(ll a,ll n)
{
    ll x,y,d=e_gcd(a,n,x,y);
    if(d==1)return (x%n+n)%n;
    else return -1;
}

费马小定理版:

当模数不为质数时,是 a ^(phi(p) - 1)

LL pow_mod(LL a, LL b, LL p){//a的b次方求余p 
     LL ret = 1;
     while(b){
         if(b & 1) ret = (ret * a) % p;
         a = (a * a) % p;
         b >>= 1;
     }
     return ret;
}
LL Fermat(LL a, LL p){//费马求a关于p的逆元 
         return pow_mod(a, p-2, p);
 }

逆元递推版

const int N = 200000 + 5;
const int MOD = (int)1e9 + 7;
int inv[N];
int init(){
    inv[1] = 1;
    for(int i = 2; i < N; i ++){
        inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD;
    }
}

普通素数筛(埃筛,复杂度O(nlog(log(n))))

void prime(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(!not_prime[i])
        {
            for(int j=i+i;j<=n;j+=i)
            not_prime[j]=true;
        }
    }
}

线性素数筛欧拉筛,复杂度O(n))

const int N=1e9+5;
const int M=1e7+5;
bool not_prime[N]={false};
int prime[M];
int tot;
void euler_sieve(int n)
{
    tot=0;
    for(int i=2;i<=n;i++)
    {
        if(!not_prime[i])prime[tot++]=i;
        for(int j=0;i*prime[j]<=n;j++)
        {
            not_prime[i*prime[j]]=true;
            if(i%prime[j]==0)break; //用最小的质因数筛,应为i%prime[j]==0,所以i=prime[j]*k,那么下一次循环要筛的是i*prime[j+1]=k*prime[j]*prime[j+1],没有必要筛,直接退出 
        }
    }
}

求单个欧拉函数

ll Euler(ll n)
{
    ll ans=n;
    for(ll i=2;i*i<=n;i++)
    {
        if(n%i==0)ans=ans/i*(i-1);
        while(n%i==0)n/=i;
    }
    if(n>1)ans=ans/n*(n-1);
    return ans;
}

普通筛法求欧拉函数(复杂度O(nlog(log(n))))

void Euler(){
    phi[1] = 1;
    for(int i = 2; i < N; i ++){
        if(!phi[i]){
            for(int j = i; j < N; j += i){
                if(!phi[j]) phi[j] = j;
                phi[j] = phi[j] / i * (i-1);
            }
        }
    }
}

线性筛法求欧拉函数(复杂度O(n))

void Euler(int n)
{
    phi[1]=1;
    int k=0;
    for(int i=2;i<=n;i++)
    {
        if(!not_prime[i])
        {
            phi[i]=i-1;
            prime[k++]=i;
        }
        for(int j=0;i*prime[j]<=n;j++)
        {
            not_prime[i*prime[j]]=true;
            if(i%prime[j]==0)//根据欧拉函数的性质。 
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            else phi[i*prime[j]]=phi[i]*phi[prime[j]];
        }
    }
}

线性筛求莫比乌斯反演函数(复杂度O(n))

void Mobius()
{
    mem(vis,false);
    mu[1]=1;
    cnt=0;
    for(int i=2;i<N;i++)
    {
        if(!vis[i])
        {
            prime[cnt++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<cnt&&i*prime[j]<N;j++)
        {
            vis[i*prime[j]]=true;
            if(i%prime[j])mu[i*prime[j]]=-mu[i];
            else
            {
                mu[i*prime[j]]=0;
                break;
            }
        }
    }
}

中国剩余定理:

int ex_gcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    int ans=ex_gcd(b,a%b,y,x);
    y-=a/b*x;
    return ans;
}
int CRT(int a[],int m[],int n)
{
    int M=1;
    int ans=0;
    for(int i=1;i<=n;i++)M*=m[i];
    for(int i=1;i<=n;i++)
    {
        int x,y;
        int Mi=M/m[i];
        ex_gcd(Mi,m[i],x,y); 
        ans=(ans+a[i]*Mi*x)%M;
    }
    if(ans<0)ans+=M;
    return ans;
}

米勒罗宾素数测试

ll mod_qmul(ll n,ll k,ll mod)
{
    ll ans=0;
    while(k)
    {
        if(k&1)ans=(ans+n)%mod;
        n=(n+n)%mod;
        k>>=1;
    }
    return ans;
} 

ll mod_qpow(ll n,ll k,ll mod)
{
    ll ans=1;
    while(k)
    {
        if(k&1)ans=mod_qmul(ans,n,mod);
        n=mod_qmul(n,n,mod);
        k>>=1;
    }
    return ans;
}

bool check(ll a,ll n,ll x,ll t) 
{
    ll ret=mod_qpow(a,x,n);
    ll last=ret;
    for(int i=0;i<t;i++)
    {
        ret=mod_qmul(ret,ret,n);
        if(ret==1&&last!=1&&last!=n-1)return true;
        last=ret;
    }
    if(ret!=1)return true;
    return false;
}

bool Miller_Rabin(ll n,int s)
{
    if(n==2)return true;
    if(n<2||(n&1)==0)return false;
    ll x=n-1,t=0;
    while((x&1)==0)x>>=1,t++;
    for(int i=0;i<s;i++)
    {
        ll a=rand()%(n-1)+1;
        if(check(a,n,x,t))return false; 
    }
    return true;
}

卢卡斯定理求组合数

void init()
{
    fac[0]=1;
    for(int i=1;i<=p;i++)fac[i]=fac[i-1]*i%p;
} 
ll q_pow(ll n,ll k)
{
    ll ans=1;
    while(k)
    {
        if(k&1)ans=ans*n%p;
        n=n*n%p;
        k>>=1;
    }
    return ans;
}
/*当p很大时*/ 
ll C(ll n,ll m)
{
    if(n<m)return 0;
    if(n-m<m)m=n-m;
    ll s1=1,s2=1;
    for(int i=0;i<m;i++) 
    {
        s1=s1*(n-i)%p;
        s2=s2*(i+1)%p;
    }
    return s1*q_pow(s2,p-2)%p;
} 
/*当p很小时 */
ll C(ll n,ll m)
{
    if(m>n)return 0;
    return fac[n]*q_pow(fac[m]*fac[n-m]%p,p-2)%p; 
} 

ll lucas(ll n,ll m)
{
    if(m==0)return 1;
    return (C(n%p,m%p)*lucas(n/p,m/p))%p;
}

 

未完待续。。。

 

以上是关于算法笔记--数论模板小集(待增)的主要内容,如果未能解决你的问题,请参考以下文章

数论算法模板(不定期更新)

数论算法模板总结

算法竞赛模板(数论)

面试高级算法梳理笔记

[数论+模板] 快速幂及快速幂求逆元算法模板(模板)

●小集训之旅 三