常用数论

Posted hooying

tags:

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

费马小定理

(a^{p-1}equiv1pmod{m} (p是质数))

求逆元

方法一:扩展欧几里得算法
前提:(a)(p)互质
原理:(a*xequiv1pmod{p} \ a*x+p*y=1)
(x)就是我们要求的逆元

LL exgcd(LL a,LL b,LL &x,LL &y)//扩展欧几里得算法 
{
    if(b==0)
    {
        x=1,y=0;
        return a;
    }
    LL ret=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return ret;
}
LL getInv(int a,int mod)//求a在mod下的逆元,不存在逆元返回-1 
{
    LL x,y;
    LL d=exgcd(a,mod,x,y);
    return d==1?(x%mod+mod)%mod:-1;
}

方法二:费马小定理
前提:(a)(p)互质且(p)为素数
原理:(a^{p-1}equiv1pmod{p} (p是质数) \ inv(a)=a^{p-2}pmod{p})


中国剩余定理

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long LL;
LL exgcd(LL a,LL b,LL &x,LL &y)
{
    if(b==0)
    {
        x=1;y=0;return a;
    }
    LL d=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        int flag=0;
        LL a1,r1,a2,r2,x,y;
        scanf("%lld%lld",&a1,&r1);
        for(int i=1;i<n;i++)
        {
            scanf("%lld%lld",&a2,&r2);
            LL c=r2-r1;
            LL d=exgcd(a1,a2,x,y);//a1*x+a2*y=gcd(a1,a2)
            if(c%d)flag=1;
            LL x0=c/d*x;//a1*(c/d*x)+a2(c/d*y)=c
            LL t=a2/d;
            x0=(x0%t+t)%t;//求a1*x0+a2*y0=c的x0的最小解(x0有可能是负数)
            r1=r1+a1*x0;
            a1=a1/d*a2;
        }
        if(flag)printf("-1
");
        else printf("%lld
",r1);
    }
    return 0;
}


欧拉函数

欧拉函数是小于x的整数中与x互质的数的个数,一般用(varphi(x))表示。特殊的,(varphi(1)=1)
通式:(varphi(x)=xprod_{i=1}^n (1-frac{1}{p_i}))

埃拉托斯特尼筛求欧拉函数

void euler(int n)
{
    for (int i=1;i<=n;i++) phi[i]=i;
    for (int i=2;i<=n;i++)
    {
        if (phi[i]==i)//这代表i是质数
        {
            for (int j=i;j<=n;j+=i)
            {
                phi[j]=phi[j]/i*(i-1);//把i的倍数更新掉
            }
        }
    }
}

欧拉筛求欧拉函数

void euler(int n)
{
    phi[1]=1;//1要特判 
    for (int i=2;i<=n;i++)
    {
        if (flag[i]==0)//这代表i是质数 
        {
            prime[++num]=i;
            phi[i]=i-1;
        }
        for (int j=1;j<=num&&prime[j]*i<=n;j++)//经典的欧拉筛写法 
        {
            flag[i*prime[j]]=1;//先把这个合数标记掉 
            if (i%prime[j]==0)
            {
                phi[i*prime[j]]=phi[i]*prime[j];//若prime[j]是i的质因子,则根据计算公式,i已经包括i*prime[j]的所有质因子 
                break;//经典欧拉筛的核心语句,这样能保证每个数只会被自己最小的因子筛掉一次 
            }
            else phi[i*prime[j]]=phi[i]*phi[prime[j]];//利用了欧拉函数是个积性函数的性质 
        }
    }
}


欧拉降幂

[egin{cases}A^Bmod(C)=A^{Bmod(phi(C))}modC (B< phi(C)) A^Bmod(C)=A^{Bmod(phi(C))+phi(C)}modC (B>= phi(C))end{cases}]
例题:求幂塔函数(a^{a^{a^{...}}}pmod{p})
题解:用递归+欧拉降幂去求

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxx = 1e6+10;
LL phi[maxx];
void euler()
{
    for(int i=1;i<maxx;i++)phi[i]=i;
    for(int i=2;i<maxx;i++)
    {
        if(phi[i]==i)//这代表i是质数
        {
            for(int j=i;j<=maxx;j+=i)
            {
                phi[j]=phi[j]/i*(i-1);//把i的倍数更新掉
            }
        }
    }
}
LL quick(LL a,LL b,LL mod)
{
    LL res=1;
    while(b)
    {
        if(b&1)res=(res*a)%mod;
        b>>=1;
        a=(a*a)%mod;
    }
    return res;
}
LL solve(LL a,LL b,LL p)
{
    if(p==1)return 0;
    if(b==0)return 1;
    LL t=solve(a,b-1,phi[p]);
    if(t<phi[p]&&t)return quick(a,t,p);
    else return quick(a,t+phi[p],p);
}
int main()
{
    euler();
    int T;
    cin>>T;
    while(T--)
    {
        LL a,b,mod;
        scanf("%lld%lld%lld",&a,&b,&mod);
        LL ans=solve(a,b,mod);
        printf("%lld
",ans%mod);
    }
    return 0;
}


二次剩余

对于(p,n),若存在整数(x),满足(x^2equiv npmod{p}),则称(n)为模(p)意义下的二次剩余
用人话说就是(n)在模(p)意义下是否能开方
勒让德符号定义如下:
[(frac{n}{p})=egin{cases}1 ,& n在模p意义下是二次剩余 \ -1,& n在模p意义下非二次剩余 \ 0,& nequiv0pmod{p}end{cases}]
对于勒让德符号,有
[(frac{n}{p})equiv n^{frac{p-1}{2}}pmod{p}(p为奇素数)]
定理一:[n^2equiv(p-n)^2pmod{p}]
定理二:(p)的二次剩余和二次非剩余的1个数均为(frac{p-1}{2})(不考虑0的情况下)

例题:https://ac.nowcoder.com/acm/contest/889/B
题意:已知(x+yequiv bpmod{p} \ x*yequiv cpmod{p}),求(x,y)
思路:((x-y)^2equiv b^2-4cpmod{p})
(a=b^2-4c),由二次剩余定理可得:(a^{frac{p-1}{2}}equiv 1pmod{p})
两边同时乘(a),可得:(a^{frac{p+1}{2}}equiv apmod{p})
((x-y)^2equiv a^{frac{p+1}{2}}pmod{p})
开平方:(x-yequiv a^{frac{p+1}{4}}pmod{p})
(x+yequiv bpmod{p})与上式相加可得:(2xequiv b+a^{frac{p+1}{4}}pmod{p})
(x=inv(2)(b+a^{frac{p+1}{4}})pmod{p})

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL p=1e9+7;
LL qpow(LL a,LL b)
{
    LL res=1;
    while(b)
    {
        if(b&1)res=res*a%p;
        a=a*a%p;
        b>>=1;
    }
    return res;
}
int main()
{
    int t;
    cin>>t;
    LL b,c;
    while(t--)
    {
        scanf("%lld%lld",&b,&c);
        LL d=b*b-4*c;
        d=(d+p)%p;//避免负数
        if(d==0)
        {
            printf("%lld %lld
",b/2,b/2);//b*b=4*c;
            continue;
        }
        if(qpow(d,(p-1)/2)!=1)
        {
            printf("-1 -1
");
            continue;
        }
        LL x=(b+qpow(d,(p+1)/4))%p*qpow(2,p-2)%p;
        LL y=b-x;
        y=(y+p)%p;
        printf("%lld %lld
",min(x,y),max(x,y));
    }
    return 0;
}


类欧几里得

用于求解[sum_{i=0}^nlfloorfrac{ai+b}{c} floor,sum_{i=0}^nlfloorfrac{ai+b}{c} floor^2,sum_{i=0}^nilfloorfrac{ai+b}{c} floor]这样的式子
模板题:https://www.luogu.org/problem/P5170
这篇博客讲的很详细:https://www.luogu.org/blog/shiqingyu/solution-p5170
直接去求的话代码如下,但因为重复计算而太耗时,所以可以用个结构体保留三者的结果

LL f(LL a,LL b,LL c,LL n) //第一条式子
{
    if(a==0)return (n+1)*(b/c)%mod;
    if(n==0)return b/c;
    if(a>=c||b>=c)return (f(a%c,b%c,c,n)+(a/c)*n%mod*(n+1)%mod*inv2%mod+(b/c)*(n+1)%mod)%mod;
    LL m=(a*n+b)/c;
    return (n*m%mod-f(c,c-b-1,a,m-1))%mod;
}
LL g(LL a,LL b,LL c,LL n) //第三条式子
{
    if(a==0)return (b/c)*n%mod*(n+1)%mod*inv2%mod;
    if(n==0)return 0;
    if(a>=c||b>=c)return (g(a%c,b%c,c,n)+(a/c)*n%mod*(n+1)%mod*(2*n+1)%mod*inv6%mod+(b/c)*n%mod*(n+1)%mod*inv2%mod)%mod;
    LL m=(a*n+b)/c;
    return (n*(n+1)%mod*m%mod-f(c,c-b-1,a,m-1)-h(c,c-b-1,a,m-1))%mod*inv2%mod;
}
LL h(LL a,LL b,LL c,LL n) //第二条式子
{
    if(a==0)return (n+1)*(b/c)%mod*(b/c)%mod;
    if(n==0)return (b/c)*(b/c)%mod;
    if(a>=c||b>=c)return (h(a%c,b%c,c,n)+(a/c)*(a/c)%mod*n%mod*(n+1)%mod*(2*n+1)%mod*inv6%mod+(b/c)*(b/c)%mod*(n+1)%mod+(a/c)*(b/c)%mod*n%mod*(n+1)%mod+2*(a/c)%mod*g(a%c,b%c,c,n)%mod+2*(b/c)%mod*f(a%c,b%c,c,n)%mod)%mod;
    LL m=(a*n+b)/c;
    return (n*m%mod*(m+1)%mod-2*g(c,c-b-1,a,m-1)-2*f(c,c-b-1,a,m-1)-f(a,b,c,n))%mod;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 998244353;
const LL inv2 = 499122177;
const LL inv6 = 166374059;
struct node
{
    LL f,g,h;
};
node solve(LL a,LL b,LL c,LL n)
{
    node ans,tmp;
    if(a==0)
    {
        ans.f=(n+1)*(b/c)%mod;
        ans.g=(b/c)*n%mod*(n+1)%mod*inv2%mod;
        ans.h=(n+1)*(b/c)%mod*(b/c)%mod;
        return ans;
    }
    if(a>=c||b>=c)
    {
        tmp=solve(a%c,b%c,c,n);
        ans.f=(tmp.f+(a/c)*n%mod*(n+1)%mod*inv2%mod+(b/c)*(n+1)%mod)%mod;
        ans.g=(tmp.g+(a/c)*n%mod*(n+1)%mod*(2*n+1)%mod*inv6%mod+(b/c)*n%mod*(n+1)%mod*inv2%mod)%mod;
        ans.h=(tmp.h+(a/c)*(a/c)%mod*n%mod*(n+1)%mod*(2*n+1)%mod*inv6%mod+(b/c)*(b/c)%mod*(n+1)%mod+(a/c)*(b/c)%mod*n%mod*(n+1)%mod+2*(a/c)%mod*tmp.g%mod+2*(b/c)%mod*tmp.f%mod)%mod;
        return ans;
    }
    LL m=(a*n+b)/c;
    tmp=solve(c,c-b-1,a,m-1);
    ans.f=(n*m%mod-tmp.f)%mod;
    ans.g=(n*(n+1)%mod*m%mod-tmp.f-tmp.h)%mod*inv2%mod;
    ans.h=(n*m%mod*(m+1)%mod-2*tmp.g-2*tmp.f-ans.f)%mod;
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    LL n,a,b,c;
    while(t--)
    {
        scanf("%lld%lld%lld%lld",&n,&a,&b,&c);
        node ans=solve(a,b,c,n);
        printf("%lld %lld %lld
",(ans.f+mod)%mod,(ans.h+mod)%mod,(ans.g+mod)%mod);
    }
    return 0;
}


斐波那契相关公式

(f_n=f_{n-1}+f_{n-2}(n>=2))
(f_n-f_{n-1}-f_{n-2}=0)
(f_n=q^n)
(q^n-q^{n-1}-q^{n-2}=0)
(q^{n-2}(q^2-q-1)=0)
故求解(q^2-q-1=0)的解
(q_1=frac{1+sqrt{5}}{2},q_2=frac{1-sqrt{5}}{2})
因为非波那契递推关系是线性的且齐次,所以对于任选常数(c_1,c_2)
(f_n=c_1(frac{1+sqrt{5}}{2})^n+c_2(frac{1-sqrt{5}}{2})^n)
随便代入两个n的数求解就行
解得斐波那契通项公式:(f_n=frac{1}{sqrt{5}}(frac{1+sqrt{5}}{2})^n-frac{1}{sqrt{5}}(frac{1-sqrt{5}}{2})^n)
故求解形如((a+sqrt{b})^n)的式子
我们会联想到斐波那契通项公式,因此构造数列
(f_n=(a+sqrt{b})^n+(a-sqrt{b})^n)
(f_n=p*f_{n-1}+q*f_{n-2})
故求解(f_2-pf-q=0)的解
我们已经知道方程的两个解(f_1=a+sqrt{b},f_2=a-sqrt{b})
带入求解p,q,最后得到(p=2a,q=b-a^2)
因此我们得到了递推式:(f_n=2acdot f_{n-1}+(b-a^2)cdot f_{n-2})

以上是关于常用数论的主要内容,如果未能解决你的问题,请参考以下文章

C#常用代码片段备忘

常用python日期日志获取内容循环的代码片段

swift常用代码片段

# Java 常用代码片段

# Java 常用代码片段

IOS开发-OC学习-常用功能代码片段整理