数论
OI Summary —— Maths
扩展欧几里德(exgcd)#
模板题#
给定 求不定方程 整数解
模板#
有解的必要条件是
inline void exgcd(int a,int b,int &x,int &y){
if(!b){
x=1,y=0;
return;
}
exgcd(b,a%b,x,y);
int t=x;
x=y,y=t-y*(a/b);
}
模板题变形#
求关于的同余方程 的最小正整数解。
求法#
实质上就是
前提是
然后解
模板#
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll x,y;
inline void exgcd(ll m,ll n){
if(!n){
x=1;
y=0;
return;
}
exgcd(n,m%n);
ll temp=y;
y=x-m/n*y;
x=temp;
return;
}
int main(){
ios::sync_with_stdio(false);
ll a,b;
cin>>a>>b;
exgcd(a,b);
cout<<(x%b+10*b)%b;
return 0;
}
乘法逆元#
定义#
若 ,则称 是 在 意义下的乘法逆元
模板题#
给定 求 中所有整数在模 意义下的乘法逆元。
费马小定理及求法#
若 为素数, 为正整数且 、 互质,则有
变形,得到:
然后用快速幂求出即可,复杂度
阶乘线性求法#
记 为 在 意义下的逆元
通过求出 就可以递推出所有
然后
模板 (阶乘线性求法)#
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e6+5;
int frac[N],c[N];
int n,p;
inline void get_frac(){
frac[0]=frac[1]=1;
for(int i=2;i<=n;++i)
frac[i]=frac[i-1]*i%p;
}
inline int qpow(int x,int idx){
if(idx==0) return 1;
int t=qpow(x,idx>>1);
return idx&1?t*t%p*x%p:t*t%p;
}
inline int get_c(int x){
return qpow(x,p-2)%p;
}
inline void get_fra_c(){
c[n]=get_c(frac[n]);
for(int i=n-1;i>=0;--i)
c[i]=c[i+1]*(i+1)%p;
}
signed main(){
scanf("%lld%lld",&n,&p);
get_frac();
get_fra_c();
for(int i=1;i<=n;++i) printf("%lld\n",frac[i-1]*c[i]%p);
}
卢卡斯(Lucas)定理#
模板题#
给定整数 的值,求出 的值, 为质数。
求法#
为答案
递归写就行
模板#
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+5;
int n,m,p;
int frac[N];
inline int qpow(int x,int idx){
if(idx==0) return 1;
int t=qpow(x,idx>>1);
if(idx&1) return t*t%p*x%p;
return t*t%p;
}
inline int C(int x,int y){
if(x<y) return 0;
return (frac[x]*qpow(frac[y],p-2))%p*qpow(frac[x-y],p-2)%p;
}
inline int Lucas(int x,int y){
if(!y) return 1;
return C(x%p,y%p)*Lucas(x/p,y/p)%p;
}
signed main(){
ios::sync_with_stdio(0);
int T;
cin>>T;
while(T--){
cin>>n>>m>>p;
frac[0]=1;
for(int i=1;i<N;++i) frac[i]=frac[i-1]*i%p;
cout<<Lucas(n+m,n)<<endl;
}
}
中国剩余定理(CRT)#
模板题#
给定 和 这 个数,求出最小的 使得 对于 均成立
其中 为正整数且 两两互质
求法#
假定
则我们可以构造出一个解
任意解 对于
最小正整数解
证明略
模板#
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=100005;
int c[N],d[N];
int n,mul=1,ans;
inline void exgcd(int a,int b,int &x,int &y){
if(!b){
x=1,y=0;
return;
}
exgcd(b,a%b,x,y);
int t=x;
x=y,y=t-y*(a/b);
}
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;++i){
scanf("%lld%lld",&c[i],&d[i]);
mul*=c[i];
}
for(int i=1;i<=n;++i){
int l=mul/c[i];
int x=0,y=0;
exgcd(l,c[i],x,y);
ans+=d[i]*l*(x<0?x+c[i]:x);
}
printf("%lld",ans%mul);
}
拉格朗日插值#
模板题#
个点 可以唯一地确定一个多项式
现在告诉你这 个点,算出多项式,并给定 求
求法#
设基本多项式
那么
然后就可以在 的时间内求解
模板#
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
const int N=2005;
int n,k,x[N],y[N],ans;
inline int qpow(int x,int idx){
if(!idx) return 1;
int t=qpow(x,idx>>1);
return idx&1?t*t%mod*x%mod:t*t%mod;
}
inline int get_c(int x){
return qpow(x,mod-2)%mod;
}
signed main(){
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;++i){
scanf("%lld%lld",&x[i],&y[i]);
}
for(int i=1;i<=n;++i){
int s1=y[i],s2=1;
for(int j=1;j<=n;++j){
if(j==i) continue;
s1=s1*(k-x[j])%mod;
s2=s2*(x[i]-x[j])%mod;
}
ans+=s1*get_c(s2)%mod;
ans=(ans+mod)%mod;
}
printf("%lld",ans);
}
大步小步(BSGS)#
模板题#
给定 求最小 满足 其中 互质
求法#
假定 , 其中
原式可化为
即
由于 互质,显然 互不相同
我们枚举 ,将 存入 表
然后枚举 ,从 表里找出第一个 满足
此时 即为所求
P3846 [TJOI2007] 可爱的质数/【模板】BSGS#
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a,b,p;
map <int,int> mp;
inline int qpow(int x,int idx){
if(!idx) return 1;
int t=qpow(x,idx>>1);
if(idx&1) return t*t%p*x%p;
return t*t%p;
}
inline int BSGS(){
b%=p;
int t=sqrt(p)+1;
for(int i=0;i<t;++i)
mp[b*qpow(a,i)%p]=i;
a=qpow(a,t);
if(!a) return !b?1:-1;
for(int i=1;i<=t;++i){
int v=qpow(a,i);
if(mp.find(v)==mp.end()) continue;
int j=mp[v];
if(i*t-j>=0) return i*t-j;
}
return -1;
}
signed main(){
scanf("%lld%lld%lld",&p,&a,&b);
int ans=BSGS();
if(ans==-1) puts("no solution");
else printf("%lld\n",ans);
}
积性函数#
定义#
互质,有 的函数 为积性函数
常见的积性函数#
: 的正因子数目
: 的所有正因子之和
:欧拉函数
:莫比乌斯函数
性质#
可以通过 来线性筛
莫比乌斯函数及反演#
莫比乌斯函数(Mobius Function)#
定义
若 则
若 则
否则
线性筛
int mu[N],prime[N],cnt;
bool vis[N];
inline void get_Mobius(int n){
memset(vis,0,sizeof(vis));
mu[1]=1;
cnt=0;
for(int i=2;i<=n;++i){
if(!vis[i]){
prime[++cnt]=i;
mu[i]=-1;
}
for(int j=1;j<=cnt&&i*prime[j]<=n;++j){
vis[i*prime[j]]=1;
if(i%prime[j]==0){
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]=-mu[i];
}
}
}
莫比乌斯反演#
结论
Miller Rabin 算法和Pollard-rho算法#
模板题#
给定一个数 判断其是否为质数,若不是输出其最大因数
求法#
这个求法有极小的概率错误,复杂度约为
Miller Rabin
前置定理:
-
费马小定理: 是质数,,那么
-
二次探测定理:对于 , 有且仅有两解 : 和
我们先找一个质数 ,若 则 为质数
然后根据费马小定理判断 是否成立,若不则 非质数
否则,我们根据二次探测定理,先用一个 记录下 ,然后只要 为偶数就持续操作:
-
先将 除以 ,然后用一个 记录下 的值。
-
如果 不等于 且不等于 ,则根据二次探测定理, 非质数。
-
如果 ,则无法继续套用二次探测定理,因此直接返回 。
Pollard rho
前置知识:Miller Rabin 算法
我们构造一个函数 用来生成随机数,其中 是一个随机的常数
但这个方法构造出的随机数数列会有循环,就像一个 一样
所以需要 判环,在有循环的时候重新随机一个
用这个方法生成随机数,然后加上 Miller Robin 的素数判断法就可以解决本题
模板#
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
int n;
ll c;
ll mul(ll p,ll q,ll x){
ll res=(long double)p/x*q;
res%=x;
return (p*q-res*x+x)%x;
}
ll getpow(ll a,ll p,ll x){
ll res=1;
while(p){
if(p&1) res=mul(res,a,x);
p>>=1;
a=mul(a,a,x);
}
return res;
}
bool judge(ll a,ll x){
ll p=x-1;
while(!(p&1))
p>>=1;
ll cur=getpow(a,p,x);
while(1){
if(p==x-1){
return cur==1;
}
ll nxt=mul(cur,cur,x);
if(nxt==1){
if(cur!=x-1 && cur!=1) return false;
}
cur=nxt;p<<=1;
}
}
bool miller_rabin(ll x){
if(x==2) return true;
if(x==1) return false;
int times=5;
while(times--){
ll a=rand()%(x-1)+1;
if(!judge(a,x)) return false;
}
return true;
}
ll next_num(ll x,ll p){
return (mul(x,x,p)+c)%p;
}
ll unsigned_abs(ll x){
if(x<0) return -x;
return x;
}
ll gcd(ll x,ll y)
{
if(!x) return y;
if(!y) return x;
ll t=__builtin_ctzll(x|y);
x>>=__builtin_ctzll(x);
do
{
y>>=__builtin_ctzll(y);
if(x>y) swap(x,y);
y-=x;
}while(y);
return x<<t;
}
ll Pollard_rho(ll x){
if(miller_rabin(x)) return x;
if(x%2==0) return max(2ll,Pollard_rho(x/2));
if(x%3==0) return max(3ll,Pollard_rho(x/3));
if(x%5==0) return max(5ll,Pollard_rho(x/5));
ll a=rand()%x;
ll b=a;
c=rand()%x;
while(1){
a=next_num(a,x);
b=next_num(next_num(b,x),x);
if(a==b){
a=rand()%x;
b=a;
c=rand()%x;
continue;
}
ll Gcd=gcd(unsigned_abs(a-b),x);
if(Gcd!=1 && Gcd!=x)
return max(Pollard_rho(x/Gcd),Pollard_rho(Gcd));
}
}
void solve(ll x){
if(miller_rabin(x)) printf("Prime\n");
else printf("%lld\n",Pollard_rho(x));
}
int main(){
srand(time(0));
cin>>n;
for(int i=1;i<=n;i++){
ll x;
scanf("%lld",&x);
solve(x);
}
}
复数#
虚数定义及表示法#
是一个虚数, 时 为实数, 且 时 为纯虚数
四则运算#
,
减法除法同理
复平面#
每一个复数都对应平面直角坐标系的一个向量
在复平面的向量坐标表示是
辐角: 与 轴正版轴夹角
模长:
加法的几何意义:两个向量相加
乘法的几何意义:辐角相加,模长相乘
单位根#
定义:数学上, 次单位根是 次幂为 的复数。它们位于复平面的单位圆上,构成正 边形的顶点,其中一个顶点是 。
表示法:,
快速傅立叶变换(FFT)#
模板题#
给定一个 次多项式 ,和一个 次多项式 。
请求出 和 的卷积。
模板#
// Problem: P3803 【模板】多项式乘法(FFT)
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3803
// Memory Limit: 500 MB
// Time Limit: 2000 ms
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const double pi=acos(-1.0);
const int N=8e6+5;
struct Complex
{
double x,y;
Complex (double a=0,double b=0){x=a,y=b;}
}f[N],g[N];
Complex operator + (Complex A,Complex B){return Complex(A.x+B.x,A.y+B.y);}
Complex operator - (Complex A,Complex B){return Complex(A.x-B.x,A.y-B.y);}
Complex operator * (Complex A,Complex B){return Complex(A.x*B.x-A.y*B.y,A.x*B.y+A.y*B.x);}
int n,m;
int l,r[N];
int mx=1;
inline void fft(Complex *A,int op){
for(int i=0;i<mx;++i)
if(i<r[i]) swap(A[i],A[r[i]]);
for(int mid=1;mid<mx;mid<<=1){
Complex W(cos(pi/mid),op*sin(pi/mid));
int R=mid<<1;
for(int j=0;j<mx;j+=R){
Complex w(1,0);
for(int k=0;k<mid;++k,w=W*w){
Complex x=A[j+k],y=w*A[j+mid+k];
A[j+k]=x+y;
A[j+mid+k]=x-y;
}
}
}
}
signed main(){
int n=read(),m=read();
for(int i=0;i<=n;++i) f[i].x=read();
for(int i=0;i<=m;++i) g[i].x=read();
for(;mx<=n+m;mx<<=1,++l);
for(int i=0;i<mx;r[i]=(r[i>>1]>>1|((i&1)<<l-1)),++i);
fft(f,1),fft(g,1);
for(int i=0;i<mx;++i) f[i]=f[i]*g[i];
fft(f,-1);
for(int i=0;i<=n+m;++i)
printf("%d ",(int)(f[i].x/mx+0.5));
}
原根Primitive Root#
定义#
假若 对于 均成立
则称 为 的原根
存在原根的数#
其中 为奇质数
求一个数的原根#
假如 是 的原根,那么对于 ,最小的 是 的约数
有关NTT特殊的数字#
NTT#
暂时略
斯特林(Stirling)数#
第一类斯特林数#
: 个元素构成 个圆排列
n | s(n,k) |
---|---|
n=0 | 1 |
n=1 | 0 1 |
n=2 | 0 1 1 |
n=3 | 0 2 3 1 |
n=4 | 0 6 11 6 1 |
生成函数: 其中 的系数表示
第二类斯特林数#
:把 个不同的球放进 个无差别的盒子中的方案数,盒子不为空
n | S(n,k) |
---|---|
n=0 | 1 |
n=1 | 0 1 |
n=2 | 0 1 1 |
n=3 | 0 1 3 1 |
n=4 | 0 1 7 6 1 |
其中 是贝尔数
通项公式:
变式
允许空盒子:
盒子有差别:
盒子有差别且可为空:
倍尔数(Bell)数#
定义及求法#
:把 个球放入若干个盒子的总方案数
贝尔三角形#
1
1, 2
2, 3, 5
5, 7, 10, 15
15, 20, 27, 37, 52
52, 67, 87, 114, 151, 203
第 条的最后一个数是 且
卡特兰(Catalan)数#
性质及求法#
1 1 2 5 14 42 132
运用#
暂时略
下降/上升幂#
定义#
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现