- 最大公因数&最小公倍数( g c d , l c m gcd,lcm gcd,lcm)
- 乘法逆元(三种方法)
- 快速乘&快速幂
- 线性筛-素数&最大质因数&最小质因数&欧拉函数
- 扩展欧几里得
- 质因数分解
- 组合数
- 高斯消元
- 矩阵快速幂
- 康拓展开
最大公约数&最小公倍数
inline int gcd(int x,int y){
int z=x%y;
while(z){x=y;y=z;z=x%y;}
return y;
}
inline int lcm(int x,int y){
return x/gcd(x,y)*y;//(先除后乘,不然容易爆int)
}
当然在求gcd的时候你也可以不用手写,可以直接调用stl的库__gcd()
乘法逆元
存在a的逆元:当且仅当
g
c
d
(
a
,
p
)
=
1
gcd(a,p)=1
gcd(a,p)=1(p为模数)(可以用扩展欧几里得来证明)
逆元的作用:模意义下的除法,可以不炸精度
1.线性递推
- 线性递推只在a<=p的时候有用!!!
因为对于大于p的数,这个就毫无意义了(仔细想一下推导的过程,不清楚可以再问哦) - 对模数没有特殊要求,但要注意数据范围是否支持O(n)
- 仔细看一下数据范围
(这道题是保证了 n < p n<p n<p,所以可以使用线性递推)
inv[1]=1;
for(int i=2;i<=n;++i)
inv[i]=(p-p/i)*1ll*inv[p%i]%p;//注意乘long long
2.扩展欧几里得
将式子转化为
a
x
+
p
y
=
1
ax+py=1
ax+py=1
x
x
x就是a在模p意义下的逆元(调整 x 到0~m-1的范围中即可)
这种算法效率较高,常数较小,时间复杂度为O(ln n)
基本上适用于求所有拟元
inline void exgcd(int a,int b,int &x,int &y){
if(!b){x=1;y=0;return;}
exgcd(b,a%b,y,x);
y-=a/b*x;
}
exgcd(a,p,x,y);
x=(x%p+p)%p;
3.费马小定理
当模数为质数的时候:
a
p
−
1
≡
1
(
m
o
d
p
)
a^{p-1}\equiv1(mod p)
ap−1≡1(modp)
即可知:
a
∗
a
p
−
2
≡
1
(
m
o
d
p
)
a*a^{p-2}\equiv1(mod p)
a∗ap−2≡1(modp)
然后直接快速幂
快速乘&快速幂
快速乘
ll ksc(ll a,ll b,ll p){
ll res=0;
while(b){
if(b&1) res=(res+a)%p;
a=(a+a)%p;
b>>=1;
}
return res;
}
快速幂
ll ksm(ll a,ll b,ll p){
ll res=1;
while(b){
if(b&1) res=ksc(res,a,p);
a=ksc(a,a,p);
b>>=1;
}
return res;
}
线性筛
pri 是素数集,mx是最大质因数,mn 是最小质因数,phi 是欧拉函数,mark 是标记一个数是否为素数(是的话,mark=0)
特别的,phi[1]=1
inline void linear_sieves(){
mark[1]=1;phi[1]=1;
for(int i=2;i<=N;++i){
if(!mark[i]){pri[++num]=i;mn[i]=i;mx[i]=i;phi[i]=i-1;}
for(int j=1;j<=num&&pri[j]*i<=N;++j){
int now=pri[j]*i;
mark[now]=1;
mn[now]=pri[j];mx[now]=mx[i];
if(i%pri[j]==0){
phi[now]=phi[i]*pri[j];
break;
}
else phi[now]=phi[i]*(pri[j]-1);
}
}
}
扩展欧几里得
对于任意的两个数x,y,求
a
x
+
b
y
=
g
c
d
(
a
,
b
)
ax+by=gcd(a,b)
ax+by=gcd(a,b)的解
扩展到更一般的情况,求
a
x
+
b
y
=
c
ax+by=c
ax+by=c也是可以的
但这个式子有解当且仅当
g
c
d
(
a
,
b
)
∣
c
gcd(a,b)|c
gcd(a,b)∣c
(平时我们用的调整,只在gcd(a,b)==1的时候适用,所以不管怎么样,全部先除以gcd(a,b),然后该怎么搞就怎么搞)
void exgcd(ll a,ll b,ll &x,ll &y){
if(!b){
x=1;y=0;return;
}
exgcd(b,a%b,y,x);
y-=a/b*x;
}
质因数分解
一个数大于
x
\sqrt{x}
x的质因数最多只有一个
所以筛到
x
\sqrt{x}
x就够了
inline void divide(int x){
for(int i=1;i<=num&&pri[i]*pri[i]<=x;++i){
while(x%pri[i]==0){
x/=pri[i];
cnt[pri[i]]++;
}
}
if(x!=1) cnt[x]++;
}
组合数
组合数的一般求解公式:
C
n
m
=
n
!
(
n
−
m
)
!
∗
m
!
C_n^m=\frac{n!}{(n-m)!*m!}
Cnm=(n−m)!∗m!n!
组合数必备三条公式:
C
n
m
=
C
n
n
−
m
C_n^m=C_n^{n-m}
Cnm=Cnn−m
C
n
m
=
C
n
−
1
m
−
1
+
C
n
−
1
m
C_n^m=C_{n-1}^{m-1}+C_{n-1}^{m}
Cnm=Cn−1m−1+Cn−1m
C
n
0
+
C
n
1
+
C
n
2
+
C
n
3
.
.
.
+
C
n
n
=
2
n
C_n^0+C_n^1+C_n^2+C_n^3...+C_n^n=2^n
Cn0+Cn1+Cn2+Cn3...+Cnn=2n
常见求解方法:
1.杨辉三角形
for(int i=0;i<2000;++i) c[i][0]=c[i][i]=1;
for(int i=1;i<2000;++i)
for(int j=1;j<i;++j)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%;
2.线性筛出阶乘和逆元,O(1) 求组合数
fac 是阶乘,ifac 是阶乘的逆元
inline void init(){
fac[0]=1;
for(int i=1;i<=n;++i) fac[i]=(fac[i-1]*1ll*i)%P;
ifac[n]=ksm(fac[n],P-2);
for(int i=n-1;i>=1;--i) ifac[i]=ifac[i+1]*1ll*(i+1)%P;
}
inline ll C(ll n,ll m){ return fac[n]*1ll*ifac[n-m]%P*ifac[m]%P;}
高斯消元
如果无解就返回-1
无穷解返回0
有解返回1,并输出一组解
inline int gauss(){
int i,j,k;
for(i=1;i<=n;++i){
k=i;
for(j=i+1;j<=n;++j) if(fabs(g[k][i])<fabs(g[j][i])) k=j;
for(j=1;j<=n+1;++j) swap(g[k][j],g[i][j]);
for(j=i+1;j<=n;++j)
for(int p=i+1;p<=n+1;++p)
g[j][p]-=g[j][i]*g[i][p]/g[i][i];
}
int fg=0;
for(i=1;i<=n;++i){
fg=0;
for(j=i;j<=n;++j) if(g[i][j]) fg=1;
if(g[i][n+1]&&fg==0) return -1;
if(g[i][n+1]==0&&fg==0) return 0;
}
for(i=n;i>=1;--i){
for(j=i+1;j<=n;++j)
g[i][n+1]-=ans[j]*g[i][j];
ans[i]=g[i][n+1]/g[i][i];
}
}
矩阵快速幂
这玩意儿用处可多了
struct matrix{
ll a[N][N];
matrix(int t=0){
memset(a,0,sizeof(a));
for(int i=1;i<=n;++i) a[i][i]=t;//为了方便地处理单位矩阵
}
inline matrix operator *(const matrix &y){
matrix res(0);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
for(int k=1;k<=n;++k)
res.a[i][j]=(res.a[i][j]+(a[i][k]%P*y.a[k][j])%P)%P;
return res;
}
friend inline matrix operator ^(matrix x,ll b){
matrix res(1);
while(b){
if(b&1) res=res*x;
x=x*x;
b>>=1;
}
return res;
}
}
康拓展开
void reverse_contor(ll x){
x--;memset(vis,0,sizeof(vis));
int i,j;
for(i=1;i<=n;++i){
int t=x/fac[n-i];
for(j=1;j<=n;++j)
if(!vis[j]){
if(!t) break;
t--;
}
b[i]=j;
vis[j]=1;
x%=fac[n-i];
}
}
ll contor(){
ll res=0;
for(int i=1;i<=n;++i){
int t=0;
for(int j=i+1;j<=n;++j) if(a[j]<a[i]) t++;
res+=1ll*t*fac[n-i];
}
return res;
}