快速幂
typedef long long ll;
ll binaryPow(ll a, ll b, ll m) {
ll ans = 1;
while (b > 0) {
if (b & 1) {
ans = ans * a % m;
}
a = a * a % m;
b >>= 1;
}
return ans;
}
唯一分解定理
打印素数表之后,遍历素数表,能除就一直除,加一个统计就行,如果过大就停止就行。
逆元
对于缩系中的元素,每个数a均有唯一的与之对应的乘法逆元x,使得ax≡1(mod n)
一个数有逆元的充分必要条件是gcd(a,n)=1,此时逆元唯一存在
逆元的含义:模n意义下,1个数a如果有逆元x,那么除以a相当于乘以x。
除一个数就等于乘这个数的逆元,模意义下似乎是不能用除法的
模的逆解线性同余方程组
ak≡1(mod m)
则对于ax≡b(mod m)两边同乘k->x=bk
费马小定理
gcd(a,p)=1 则ap-1≡1(mod p)
也就是a*ap-2≡1(mod p) ap-2是a模p的一个逆
欧几里得算法
int gcd(int a,int b){
return b == 0 ? a : gcd(b, a%b);
}
gcd和lcm
由唯一分解定理可得,lcm(a,b)gcd(a,b)=ab。
注意已知gcd和a*b求lcm这种记得先除避免爆了。
素数定理
小于或等于x的素数个数,称为π(x),随着x增大,有:
π(x)≈x/ln(x)
扩展欧几里得算法
也就是在原先求gcd的基础上,还求出了gcd(a,b)=ax+by中的x y
先看一个推导
ax+by=bx-(a%b)y
=bx+(a-int(a/b)b)y
=ay+b(x-int(a/b)y)
现在的等式两边的x y是不一样的,分别对应一组x y的解,那x y就可以这样一组解一组解的依次转化,而我们可以直到当它一直向下递归的时候一定能达到x=1,y=0的这组解,所以我们就可以得到所有解了
int exGcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;y=0;
return a;
}
int r=exGcd(b,a%b,x,y);
int t=x;x=y;y=t-a/b*y;
return r;
}
求逆元的几种方法求乘法逆元的几种方法
exgcd求逆元
用扩展欧几里得算法求得一组x0,y0和gcd
检查gcd是否为1
gcd不为1则说明逆元不存在
若为1,则调整x0到0~m-1的范围中即可
exgcd求ax=c(mod b)的x的最小整数解
我们可以用扩展欧几里得算法得出ax+by=gcd(a,b) 的一组解(x1,y1),那么其他解呢?任取另一组解(x2,y2),则ax1+by1=ax2+by2(因为它们都等于gcd(a,b) ),变形得a(x1-x2)=b(y2-y1)。假设gcd(a,b)=g,方程左右两边同时除以g(如果g=0,说明a或b等于0,可以特殊判断),得a’(x1-x2)=b’(y2-y1),其中a’=a/g,b’=b/g。注意,此时a’和b’互素(想想分数的化简),则因此x1-x2一定是b’的整数倍(因为a’中不包含b’,所以x1-x2一定包含b’)。设它为kb’,计算得y2-y1=ka’。注意,上述的推导过程并没有用到“ax+by的右边是什么”,因此得出以下结论:
设a,b,c为任意整数,若方程ax+by=c的一组解是(x0,y0),则它的任意整数解都可以写成(x0+kb’,y0-ka’),其中a’=a/gcd(a,b),b’=b/gcd(a,b),k取任意整数。
int cal(int a,int b,int c)
{
int x,y;
int gcd=(a,b,x,y);
if(c%gcd!=0)
return -1;//代表无解
// ax0+by0=gcd(a,b) 方程一
//同时乘以c/gcd(a,b)得
// (a*c/gcd(a,b))*x0+(b*c/gcd(a,b))*y0=c;
// 令 x1=c/gcd(a,b)*x0 y1=c/gcd(a,b)*y0;
// 则可得 ax1+by1=c 方程二
// 这时得出方程的一个解 x1=x0*c/gcd(a,b) y1=y0*c/gcd(a,b)
x*=c/gcd; //将 方程一的一个特解转化成方程2的一个特解
//套用上文的公式可得对方程二
// b'=b/gcd(a,b);
b/=gcd;
if(b<0)//处理小于0的特殊情况
b=-b;
//对特解x +- kb' 找到最小整数解
//设x=kb'+r
//那么我们想要求的整数解就是r
//直接取模运算即可
int ans=x%b;
//把负数的r转化成正数的
if(ans<=0)
ans+=b;
return ans;
}
https://blog.csdn.net/weixin_42165981/article/details/81185418
线性同余方程组
1.两个以上不同模的一元线性同余方程
2.变元数大于1,方程数大于1,但方程模相同
对于1问题可以用中国剩余定理
对于2问题可以用高斯消元法
中国剩余定理
对于不同模的一元线性方程组
x≡a1(mod m1)
x≡a2(mod m2)
····
x≡ar(mod mr)
有M=m1m2····*mr
那我就令Mi=m1* m2*··· *mi-1 *mi+1 *···*mr yi为其逆元
则x=a1M1y1+a2M2y2+····+arMryr(mod M) 且唯一(有两解可推相等)
因为对于任意mod mi,mi | Mk(k!=i) 而对于aiMiyi, Miyi=1
所以x就是解
int china(int r)
{
M=1;
for(int i=1; i<=r; i++)
M*=m[i];
for(int i=1; i<=r; i++)
{
Mi=M/m[i];
extend(Mi,m[i],d,X,Y);
ans=(ans+Mi*X*a[i])%M;
}
if(ans<0)
ans+=M;
return ans;
}
当然也可以用迭代法,就是和普通算数一样,就一个方程一个方程带
//多项式同余方程
欧拉函数
对正整数n,欧拉函数是小于或等于n的正整数中与n互质的数的数目
欧拉定理
aφ(n)≡1(mod n)
用既约剩余系证明
由此又可以得出求逆的方法,aφ(n)-1即为a的逆
定理1:若p为素数 则φ§=p-1 逆定理亦然
定理2:p是素数 a是正整数φ(pa)=pa-pa-1(不超过pa且能被p整除的即kp(1<=k<=pa-1)
定理3:设m n互素,则φ(mn)=φ(m)φ(n) 积性函数
定理4:设n=p1a1p2a2···pnan为正整数n的唯一分解式,则φ(n)=n(1-1/p1)(1-1/p2)···(1-1/pn)
利用定理2对每个质因子单独考虑p1a1->p1a1-1(p1-1)=p1a1(1-1/p1)
乘起来就是答案
定理5:n的所有因子d的φ(d)加起来为n
筛法
思想很简单,对于不超过n的非负整数p,删除2p,3p,4p····,当处理完所有数之后,还没有被删除的就是素数。
但这做了很多无用功。
下面给出对其的三次优化
1.第一层循环只需要到n1/2就行,比如n=10000,那对于一个100-10000的数,如果一个因子在100-10000,另一个因子一定<100。
2.增加vis数组不仅仅用于标记,还用于判重,因为第一层循环可能循环到比如4,那我在2的时候就已经标记过4了,所以跳过。
3.第二层循环的开始从i2开始,比如第一层循环为3,那么13和23在前面一定已经标记过了,所以只需要从此开始就行。
int m=sqrt(n+0.5);
memset(vis,0,sizeof(vis));
for(int i = 2; i<= m; i++) if(!vis[i])
for(int j = i*i; j<=n; j+=i) vis[j]=1;
然而埃筛是有缺陷的:对于一个合数,有可能被筛多次,所以我们限定只用某一个数的最小质因子取筛选即可,即为欧拉筛
线性筛 欧拉筛
int prime[maxn];
int visit[maxn];
void Prime(){
mem(visit,0);
mem(prime, 0);
for (int i = 2;i <= maxn; i++) {
if (!visit[i]) {
prime[++prime[0]] = i; //这个prime[0] 相当于 cnt,用来计数
}
for (int j = 1; j <=prime[0] && i*prime[j] <= maxn; j++) {
visit[i*prime[j]] = 1;
if (i % prime[j] == 0) {
break;
}
}
}
}
这里是把i当作要乘的数,也就是用素数表中的数取乘i,然后i % prime[j] == 0就是当 i是prime[j]的倍数时,i = k*prime[j],如果继续运算 j+1,i * prime[j+1] = prime[j] * k prime[j+1],这里prime[j]是最小的素因子,当i = k * prime[j+1]时会重复,所以才跳出循环。不只是对于prime[j+1],往后都可以在i=kprime[j+m]中被更小的质因子prime[j]更新
线性筛求积性函数
求欧拉函数
int prime[maxn];
int vis[maxn];
int phi[maxn];
void Prime() {
memset(vis, 0, sizeof(vis));
memset(prime, 0,sizeof(prime));
for (int i = 2; i <= maxn; i++) {
if (!vis[i]) {
prime[++prime[0]] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= prime[0] && i * prime[j] <= maxn; j++) {
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) {
phi[i * prime[j]] = phi[i] * prime[j];//phi[p^k]=(p-1)*p^(k-1),i中已经*了p-1,那后面只需一直除乘p就行了,可以想象递推的过程
break;
}
else phi[i * prime[j]] = phi[i] * (prime[j] - 1);//i中无p,*(p-1)即phi[prime[j]]
}
}
}
排列组合
集合的排列就高中题
集合的循环排列就是围成一个圆的排列,就拿n去除线性排列的数目即可
A(n,r)=r!(n r)逻辑上非常好理解
那么(n,r)=n!/r!(n-r)!
多重集的排列
S是一个多重集,有k个不同类型的元素,各元素的重数分别为n1,n2,···nk,则S的排列数为n!/n1!n2!····*nk!
就是有nk个元素相同,那位置互换也相同,所以除掉
多重集的组合
也就是x1+x2+···+xk=r的非负整数解个数 xi取无限
那我们就先解决正整数解个数,就是用隔板法就是C(r-1,k-1)
然后非负整数解就是令yi=xi+1 然后再用隔板法就是C(r+k-1,k-1)=(r+k-1,r)
x1+x2+x3+x4=20的整数解个数 x1>=3,x2>=1, x3>=0, x4>=5
那就分别用不同的yi去替换就行了
Pascal公式
C(n, k) = C(n - 1, k - 1) + C(n - 1, k)
结合实际意义推导就是对于第n个物品,要么选要么不选。加一块就是了
当然也可以直接算,也简单
预处理二项式系数就用Pascal公式递归
for (int i = 0; i <= n; i++) {
for (int j = 0; j < i + 1; j++) {
if (i == j || j == 0) {
arr[i][j] = 1;
} else {
arr[i][j] = arr[i - 1][j - 1] + arr[i - 1][j];
}
}
两个恒等式
C(0,m)+C(1,m)+···+C(n,m)=C(n+1,m)
kC(n,k)=nC(n-1,k-1)
多项式定理
(x1+x2+x3+···+xk)n=∑n!/(n1!*n2!n3!···*nk!)x1n1x2n2···xnnk
组合数计算
在数据范围比较小的时候我可以用pascal公式O(n2)计算
但是比较大就需要用组合数公式C(n,m)=n!/(m!(n-m)!)
先预处理阶乘fact[i]=fact[i-1]*i
然后令分母等于k,计算k mod p的逆元,然后快速幂求解O(logn)即可
也可以采用递推方式
也可以Lucas定理
111这里大佬博客有详解
容斥原理
S中不具有P1P2···Pn的物体个数
也就是S-∑|Ai|+∑|Ai∩Aj|····奇减偶加这样
至少具有一个就拿全集再去减这个就行
还有|A∪B|=|A|+|B|-|A∩B|奇加偶减
高斯消元法
解多元方程组,也就是把方程组写成矩阵形式,然后把系数矩阵变成上三角矩阵再依次推出答案
https://blog.csdn.net/lzyws739307453/article/details/89816311