数学基础算法复习

基础

阶乘

0 ! = 1 0!=1 0!=1

组合数

a ( n , m ) = n ! ( n − m ) ! a(n,m)= \frac{n!}{(n-m)!} a(n,m)=(nm)!n!

$c(n,m)= \frac{n!}{(n-m)!m!} $

c ( n , m ) = c ( n − 1 , m − 1 ) + c ( n , m − 1 ) c(n,m)=c(n-1,m-1)+c(n,m-1) c(n,m)=c(n1,m1)+c(n,m1)

快速幂

ll qp(ll b,ll p,ll mod){
	ll tmp=b,ans=1;
	while(p){
		if(p&1)
		ans=(ans%mod*tmp%mod)%mod;
		tmp=(tmp%mod*tmp%mod)%mod;
		p>>=1;
	}
	return ans%mod; 
} 

快速乘

ll mil(ll a,ll b,ll p){
	ll ans=0;
	a%=p;
	b%=p;
	while(b!=0){
		if(b&1){
			ans+=a%p;
		}
		b>>=1;
		a=(a<<1)%p; 
	}
	return ans%p;
}

素数和约数

线性筛

一种非常朴素的想法:每一个筛出的质数的倍数都不是质数。

进一步:每一个筛出的质数的质数倍数都不是质数且这样算不会有遗漏

这就是线性筛。

素数个数

一个长度为 n n n 渐进估计有 n l n ( n ) \frac{n}{ln(n)} ln(n)n 个质数

欧拉函数

读作“phi”

ϕ ( n ) \phi(n) ϕ(n) 表示 n n n 以内与 n n n 的互素的数的个数。

一些性质(筛法求的基础)

i i i , j j j 互质, ϕ ( i × j ) = ϕ ( i ) × ϕ ( j ) \phi(i\times j)=\phi(i)\times \phi(j) ϕ(i×j)=ϕ(i)×ϕ(j)

i i i 为质数, ϕ ( i ) = i − 1 \phi(i)=i-1 ϕ(i)=i1

a a a 为质数, ϕ ( a p ) = ( a − 1 ) ∗ ϕ ( a p − 1 ) \phi(a^p)=(a-1)*\phi(a^{p-1}) ϕ(ap)=(a1)ϕ(ap1)(这个要注意)

求单个欧拉函数

基于这样一个结论( p i p_i pi 表示第 i i i 个素数),设 a = p a 1 c 1 p a 2 c 2 p a 3 c 3 . . . a=p_{a_1}^{c_1}p_{a_2}^{c_2}p_{a_3}^{c_3}... a=pa1c1pa2c2pa3c3...,则有 $\phi(a)=(1-\frac{1}{p_{a_1}}) (1-\frac{1}{p_{a_2}})… $

代码(网上贺的):

long long eular(long long n)
{
    long long ans = n;
    for(int i = 2; i*i <= n; i++)
    {
        if(n % i == 0)
        {
            ans -= ans/i; //等价于通项,把n乘进去
            while(n % i == 0) //确保下一个i是n的素因数
                n /= i;
        }
    }
    if(n > 1)ans -= ans/n; //最后可能还剩下一个素因数没有除
    return ans;
}

线性筛求欧拉函数

基于上面的结论,可得代码(还是网上贺的):

void Phi()
{
	for(int i=2;i<=n;i++)
	{
		if(!vis[i]) prim[++pn]=i,phi[i]=i-1;
		for(int j=1;j<=pn&&i*prim[j]<=n;j++)
		{
			vis[i*prim[j]]=1;
			if(i%prim[j]==0)
			{
				phi[i*prim[j]]=phi[i]*prim[j];
				break;
			}
			else phi[i*prim[j]]=phi[i]*(prim[j]-1);
		}
	}
} 

逆元

定义

a x ax ax 1 1 1 关于 p p p 同余。

用于处理 a b \frac{a}{b} ba

a b = a ∗ i n v ( b ) \frac{a}{b}=a*inv(b) ba=ainv(b)

费马小定理

a p − 1 a^{p-1} ap1 1 1 1 关于 p p p 同余(当且仅当 p p p 为素数)。

此时 x = a p − 2 x=a^{p-2} x=ap2 m o d mod mod p p p

递推

先贴公式(贺的),证明咕。

inv[1] = 1;
for(int i = 2; i < p; ++ i)
    inv[i] = (p - p / i) * inv[p % i] % p;

想不到吧!没有证明!

整除分块

因为最近做得多所以写一下

数学分块大部分时候用于处理

∑ i = 1 n ⌊ n i ⌋ \sum_{i=1}^n \lfloor \frac{n}{i} \rfloor i=1nin

这样类型的柿子,很多情况下藏得非常隐蔽。

r=a[i]/(a[i]/l)

矩阵

矩阵乘法


for(int k=1;k<=n;k++)
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			c[i][j]=(c[i][j]+a[i][k]*a[k][j])%mod;
		}
	}

矩阵快速幂

基于快速幂

#include<bits/stdc++.h>
using namespace std;
#define int long long 
const long long mod=1e9+7;
long long n,k,a[105][105],ans[105][105],c[105][105];
void times1(){
	memset(c,0,sizeof(c));
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++){
			c[i][j]=(c[i][j]+ans[i][k]*a[k][j])%mod;
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++){
			ans[i][j]=c[i][j];
		}
}
void times2(){
	memset(c,0,sizeof(c));
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				c[i][j]=(c[i][j]+a[i][k]*a[k][j])%mod;
			}
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++){
			a[i][j]=c[i][j];
		}
}
signed main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		//ans[i][i]=1;
		for(int j=1;j<=n;j++){
		cin>>a[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		ans[i][i]=1;
	}
	while(k){
		if(k&1){
			times1();
		}
		times2();
		k>>=1;
	} 
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
		cout<<ans[i][j]%mod<<" ";
		}
		puts("");
	}
	return 0;
}

矩阵乘法优化DP

1)线性递推式可以构造矩阵来用快速幂优化。

2)柿子长得像矩阵乘法的(明确指出

容斥原理

待填坑

数学期望

待填坑

一些奇怪的算法

扩展欧几里得

即俗称的 exgcd。

用于求解

a x + b y = d ax+by=d ax+by=d

这样的柿子。

我们先令 d = g c d ( a , b ) d=gcd(a,b) d=gcd(a,b)

按照 gcd 的套路推一下:

我们有 gcd ⁡ ( b , a ( m o d b ) ) = gcd ⁡ ( a , b ) \gcd(b,a \pmod b)=\gcd(a,b) gcd(b,a(modb))=gcd(a,b)

由此可得 a x + b y = gcd ⁡ ( a , b ) = gcd ⁡ ( b , a ( m o d b ) ) = b × t x + a ( m o d b ) × t y ax+by=\gcd(a,b)=\gcd(b,a \pmod b)=b \times tx+ a \pmod b \times ty ax+by=gcd(a,b)=gcd(b,a(modb))=b×tx+a(modb)×ty

按照取模的一般套路,可得 x = t y , y = t x − ⌊ a / b ⌋ × t y x=ty,y=tx-\lfloor a/b \rfloor \times ty x=ty,y=txa/b×ty

代码(贺的)

ll exgcd(ll a,ll b,ll &x,ll &y)
{
	if(!b)
	{
		x=1;y=0;
		return a;
	}
	else
	{
		ll tx,ty;
		ll d=exgcd(b,a%b,tx,ty);
		x=ty;y=tx-(a/b)*ty;
		return d;
	}
}
————————————————
版权声明:本文为CSDN博主「zsyz_lb2003」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zyszlb2003/article/details/89337144

欧拉定理

要不是古代猪文我都不知道有这东西

a ϕ ( m ) a^{\phi(m)} aϕ(m) 1 1 1 关于 m m m 同余。

前提条件: a a a m m m 互质。

俺寻思费马小定理就是欧拉定理一特殊情况

重要的是,欧拉定理可以推出一推论:

扩展欧拉定理

我不晓得,只会贺。

卢卡斯定理

不怎么常用但还是挺有用的东西

定理是这样的:

C n k m o d p = C n / p k / p C n m o d p k m o d p m o d p C_{n}^{k}\quad mod \quad p=C_{n/p}^{k/p} C_{n \quad mod \quad p}^{k \quad mod \quad p}\quad mod \quad p Cnkmodp=Cn/pk/pCnmodpkmodpmodp

写的好丑啊

其中 p p p 必须是个质数。

代码( C C C 的计算方式

inline ll comb(ll n, ll m, ll mod){
	ll f1=1,f2=1;
	for (ll i=1,j=n;i<=m;i++,j--){
		f1=f1*j%mod;
		f2=f2*i%mod;
	}
	return f1*qp(f2,mod-2,mod)%mod;
}
ll lucas(ll m, ll n, ll mod){
	if(n==0) 
	return 1;
	return lucas(m/mod,n/mod,mod)*comb(m%mod,n%mod,mod)%mod;
}

中国剩余定理

又叫孙子定理,用于求解一系列的模数方程组,形式化的描述戳这里注意 CRT 的模数必须两两互质

顺次计算每个方程,设目前算出前 k − 1 k-1 k1 个方程的解为 x k − 1 x_{k-1} xk1 m m m 是前 k − 1 k-1 k1 个模数的 L C M LCM LCM,那么显然前 k − 1 k-1 k1 个方程的通解显然为 x k − 1 + t m x_{k-1}+tm xk1+tm

接着考虑第 k k k 个方程,由于要满足上述条件,设 x k = x k − 1 + t m x_k=x_{k-1}+tm xk=xk1+tm

那么可以解方程 x k − 1 + t m ≡ a k ( m o d b k ) x_{k-1}+tm \equiv a_k \pmod {b_k} xk1+tmak(modbk)

使用 exgcd!

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[200],m[200],M=1,Mi[200],x,y,ans;
void exgcd(int a,int b){
	if(b==0)
	{
		x=1;
		y=0;
		return ; 
	}
	exgcd(b,a%b);
	long long z=x;
	x=y;
	y=z-(a/b)*y;
}
signed main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>m[i];
		M*=m[i];
		cin>>a[i];
	}
	for(int i=1;i<=n;i++){
		Mi[i]=M/m[i];
		x=y=0;
		exgcd(Mi[i],m[i]);
		int t;
		if(x<0)
		t=x+m[i];
		else
		t=x;
		ans+=a[i]*Mi[i]*t;
	}    
	cout<<ans%M<<endl; 
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值