数论小集(2)

数论小集(进阶篇)

1.唯一分解定理

任意一个大于1的正整数都能被表示成若干个素数的乘积且表示方法是唯一的;整理可以将相同素数的合并;可以得到

公式:n = P1^a1 * P2^a2 * …………* Pn^an(P1 < P2 < ……Pn);

用代码求一个数唯一分解后的式子我们可以先打素数表,在一直用质数去除它就行了。

代码:

void Prime()
{
	cnt=0;
	memset(vis,0,sizeof(vis));
	for(int i=2;i<=N;i++)
	{
		if(!vis[i])
		prime[cnt++]=i;
		for(int j=0;j
(例如,e={1,0,2,0,0,…}表示2^1*5^2=50)

至于唯一分解定理的用处:

1.可以证明a*b=gcd(a,b)*lcm*(a,b)(好像没啥用。。。。)

2.可以降低运算的数量级。大家都知道,数论的题一般规模很大,也许答案不会很大,但在求答案过程中乘着乘着就溢出了,所以产生了许多奇技淫巧来解决这种问题,唯一分解定理就是其中一个。特别是在有不能取余的除法时,可以通过质因子的约分得出答案。

例题:UVA10375

题意是求C(p,q)/C(r,s),保留五位小数,自然是用阶乘来求排列,再唯一分解一下,约个分,美滋滋。

代码:

#include
#include
#include
using namespace std;
const int N=10001;
int e[N],vis[N],prime[N];
int a,b,c,d,cnt;
void Prime()
{
	cnt=0;
	memset(vis,0,sizeof(vis));
	for(int i=2;i<=N;i++)
	{
		if(!vis[i])
		prime[cnt++]=i;
		for(int j=0;j>=1;
	}
	if(flag) return 1.0/res;
	return res;
}
void adde(int num,int d)
{
	for(int i=0;i

2.模线性方程(简易)

数论最重要的之一!!
一般形式为解axb(mod n)(注意:”为同余,a≡b(mod n)意为a和b关于模n同余,a-kn=b(k为整数)
则原来的方程可以化为ax-ny=b,这个方程可能有多个解,也有可能无解。而要解决它,没错,就是用扩展欧几里得,所以我们直接用扩欧就可以求出ax0-ny0=gcd(a,n), 此时只需要利用上次讲的两个结论,来判断是否有解,并且将解转化到题目中限制范围(比如说求非负数解啊)

3.逆元

我们在取模运算中,是没有除法的,但是这怎么能阻止数论发展的脚步,所以数论大佬们就发明了逆元!
对于正整数,如果有,那么把这个同余方程中的最小正整数解叫做的逆元。
求逆元有以下几种方法:
1.上面的模线性方程就起作用了,既然,那么ax-my=1,当a与m互质时,那我们可以用扩欧求出x,x便是a关于m的逆元了。
2.费马小定理。费马小定理其实欧拉定理的一个子情况,至于欧拉定理,emm。。。反正带欧拉二字的东西都很厉害
这里只是提一下。咳,回到正题,费马小定理:

假如p是质数,且gcd(a,p)=1,那么 a(p-1)≡1(mod p),即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。(摘自百度百科)

则可推得:a^(m-1)≡1(mod m)--->a*a^(m-2)≡1(mod m)--->a^(m-2)≡1/a(mod m)
所以我们只需要用快速幂取模就行了。
3.线性推逆元。这个可以去看这个链接点击打开链接,这里我只给出递推式:
inv[i] = ( MOD - MOD / i ) * inv[MOD%i] % MOD
4.除法取模的通用式:ans=(a/b)%mod=a%(m*b)/b;
证明如下:
a/b=k*m+x(x
a=k*b*m+b*x
a%(b*m)=b*x
a%(b*m)/b=x
证得。
代码:
//b/a=b*inv(a)(mod m) 
int poww(int base,int b)
{
	int res=1;
	while(b)
	{
		if(b&1) res=res*base%m;
		base=base*base%m;
		b>>=1;
	}
	return res;
} 
void gcd(int a,int b,int& g,int& x,int &y)
{
	if(!b) {g=a;x=1;y=0;}
	else {gcd(b,a%b,g,y,x);y-=x*(a/b);}
}
int inv1()//线性递推逆元 
{
	inv[1]=1;
	for(int i=2;i<=n;i++)
	inv[i]=(m-m/i)*inv[m%i]%m; 
}
int inv2()//扩展欧几里得求逆元 
{
	int x,y,g;
	gcd(a,m,g,x,y);
	return x;
} 
int inv3()//费马小定理求逆元 
{
	return poww(a,m-2)%m;
}
int inv4(int a,int b)//取余除法通用法 
{
	//ans=a/b(mod m)
	ans=a%(m*b)/b;
} 


以上算法各有所长,也有各有各的缺点,如扩欧和费马小定理只能在m为质数才行。

4.卢卡斯定理

卢卡斯定理用于较大的组合数取模,有: Lucas定理:我们令 n=sp+q , m=tp+r  . (q ,r ≤p)p为素数
那么:
一般来讲,使用这个定理一般是在n,m特别大,而p比较小的时候,对于n,m小于p时,我们需要打阶乘表,用组合数的基础算法来求,其余情况在代码实现时只需要一直递归就行了。
代码:
#include
#include
#include
#define LL long long
using namespace std;
LL mod,n,m;
LL fac[10001];
void factor(){
	fac[0]=1;
	for(LL i=1ll;i<=mod;i++)
	fac[i]=(fac[i-1]*i)%mod;
}
LL poww(LL base,LL b)
{
	LL res=1ll;
	while(b)
	{
		if(b&1) res=(res*base)%mod;
		base=(base*base)%mod;
		b>>=1;
	}
	return res;
}
LL C(LL x,LL y){
	if(y>x) return 0;
	return (fac[x]*poww(fac[y]*fac[x-y],mod-2))%mod;
}
LL lucas(LL x,LL y){
	if(y==0) return 1ll;
	return (C(x%mod,y%mod)*lucas(x/mod,y/mod))%mod;
}
int main()
{
	//freopen("a.in","r",stdin);
	scanf("%lld %lld %lld",&n,&m,&mod);
	factor();
	printf("%lld",lucas(n,m));
 	return 0;
}


没错,这次大部分都是和取模有关的,取模可是重中之重,难上加难。。反正咸鱼脑子现在都还记不全逆元算法。。。

你可能感兴趣的:(我爱数论,数论爱我)