常用数论
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})
以上是关于常用数论的主要内容,如果未能解决你的问题,请参考以下文章