常用数论
费马小定理
\(a^{p-1}\equiv1\pmod{m}\ (p是质数)\)
求逆元
方法一:扩展欧几里得算法
前提:\(a\)和\(p\)互质
原理:\(a*x\equiv1\pmod{p} \quad 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}\equiv1\pmod{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\n");
else printf("%lld\n",r1);
}
return 0;
}
欧拉函数
欧拉函数是小于x的整数中与x互质的数的个数,一般用\(\varphi(x)\)表示。特殊的,\(\varphi(1)=1\)
通式:\(\varphi(x)=x\prod_{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]];//利用了欧拉函数是个积性函数的性质
}
}
}
欧拉降幂
例题:求幂塔函数\(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\n",ans%mod);
}
return 0;
}
二次剩余
对于\(p,n\),若存在整数\(x\),满足\(x^2\equiv n\pmod{p}\),则称\(n\)为模\(p\)意义下的二次剩余
用人话说就是\(n\)在模\(p\)意义下是否能开方
勒让德符号定义如下:
对于勒让德符号,有
定理一:$$n2\equiv(p-n)2\pmod{p}$$
定理二:\(p\)的二次剩余和二次非剩余的1个数均为\(\frac{p-1}{2}\)(不考虑0的情况下)
例题:https://ac.nowcoder.com/acm/contest/889/B
题意:已知\(x+y\equiv b\pmod{p} \\ x*y\equiv c\pmod{p}\),求\(x,y\)
思路:\((x-y)^2\equiv b^2-4c\pmod{p}\)
令\(a=b^2-4c\),由二次剩余定理可得:\(a^{\frac{p-1}{2}}\equiv 1\pmod{p}\)
两边同时乘\(a\),可得:\(a^{\frac{p+1}{2}}\equiv a\pmod{p}\)
故\((x-y)^2\equiv a^{\frac{p+1}{2}}\pmod{p}\)
开平方:\(x-y\equiv a^{\frac{p+1}{4}}\pmod{p}\)
将\(x+y\equiv b\pmod{p}\)与上式相加可得:\(2x\equiv 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\n",b/2,b/2);//b*b=4*c;
continue;
}
if(qpow(d,(p-1)/2)!=1)
{
printf("-1 -1\n");
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\n",min(x,y),max(x,y));
}
return 0;
}
类欧几里得
用于求解$$\sum_{i=0}^n \lfloor \frac{ai+b}{c} \rfloor,\sum_{i=0}^n \lfloor\frac{ai+b}{c}\rfloor^2 ,\sum_{i=0}^n i\lfloor\frac{ai+b}{c}\rfloor$$这样的式子
模板题: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\n",(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=2a\cdot f_{n-1}+(b-a^2)\cdot f_{n-2}\)