萌新学数论啦(一)

一.基本概念:

整除性:

一个整数a能够被另一个整数d整除,记作 d|a,意味着对于某个整数k,有a = k*d。

如果d|a,且d >= 0,则d为a的一个约数。

对于任意整数x, y,有 d|a 且 d|b 则 d|(ax+by)

每个整数a都可以被其平凡约束1和a整除,a的非平凡约数也称为a的因子。

公约数;

两个不同时为0的整数a和b的最大公约数表示成gcd(a, b),有gcd(0,0) = 0,  gcd(a,0) = |a|;

定理:∀a,b∈N, a,b ≠ 0则gcd(a,b)是a与b的线性组合集合{ax+by: x,y∈Z}中的最小正元素

如果gcd(a,b) = 1,则a与b称为互质数,有:gcd(a,p) = gcd(b,p) = 1,则gcd(ab,p) = 1。

唯一因子的分解:

定理:对于所有素数p和所有整数a和b,如果p|ab,则p|a 或 p|b(或两者都成立)。

定理:合数a仅能以一种方式,写成如下乘积的形式:    

其中pi为素数,p1 < p2 < ... < pr, 且ei为正整数

二.最大公约数:

gcd递归定理:对于任意非负整数a和正整数b,gcd(a,b) = gcd(b, a%b);

Bezout定理:对于任何整数a和b和它们的的最大公约数d,关于未知数x和y的线性丢番图方程:ax+by = m,

有整数解时当且仅当m是d的倍数。

代码模板:

int gcd(int a, int b)
{
	if(b == 0)
		return a;
	else
		return gcd(b, a%b);
}

int exgcd(int a, int b, int &x, int &y)
{
	if(b == 0){
		x = 1, y = 0;
		return a;
	}
	int d = exgcd(b, a%b, y, x);
	y -= a/b * x;
	return d;
}

最小公倍数:lcm(a,b),有 ab = gcd(a,b) * lcm(a,b);    lcm(S/a, S/b) = S/gcd(a, b)

三.素数筛,快速幂:

int prime[N], vis[N], cnt;	//cnt表示[1,N]中素数个数 
void init(){
    memset(vis, 0, sizeof(vis))
    for(int i = 2; i <= N; i ++){
    	if(!vis[i])
    		prime[cnt++] = i;     
        for(int j = i*i; j < N; j += i){
            vis[j] = 1;
        }
    }
}
该算法的时间复杂度为O(nloglogn)
LL pow_mod(LL a, LL b, LL p)
{ 
    LL ret = 1;
    while(b){
        if(b & 1) 
			ret = (ret * a) % p;
        a = (a * a) % p;
        b >>= 1;
    }
    return ret;
}

四.欧拉函数:

欧拉定理:若n,a为正整数且互质,则a^φ(n) ≡ 1 (mod n),φ(n)为欧拉函数,欧拉函数是求在1到n-1中与n互质的数的数目。

欧拉函数是积性函数,即若m,n互质,有f(mn) = f(m)*f(n).(所有的幂函数都是积性函数)


//求某个数的欧拉函数值
int getphi(int n)
{
	int m = sqrt(n+0.5);
	int ans = n;
	for(int i = 2; i <= m; i++){
		if(n % i == 0){
			ans = ans/i * (i-1);
			while(n%i == 0){
				n /= i;
			}
		}
	}
	if(n > 1)
		ans = ans/n * (n-1);
	return ans;
} 

//计算1~n的所有数字的欧拉函数值,O(n)
const int N = 1e6+10 ;
int phi[N], prime[N];
int tot;//tot计数,表示prime[N]中有多少质数 
void Euler(){
    phi[1] = 1;
    for(int i = 2; i < N; i ++){
        if(!phi[i]){
            phi[i] = i-1;
            prime[tot ++] = i;
        }
        for(int j = 0; j < tot && 1ll*i*prime[j] < N; j ++){
            if(i % prime[j]) phi[i * prime[j]] = phi[i] * (prime[j]-1);
            else{
                phi[i * prime[j] ] = phi[i] * prime[j];
                break;
            }
        }
    }
}

一个类似埃筛的写法,速率没有上面的快(但是好写啊~)

euler[1]=1;    
for(int i = 2;i < Max;i++)    
    euler[i]=i;    
for(int i = 2;i < Max;i++)    
    if(euler[i] == i)    
        for(int j = i;j < Max; j+=i)    
            euler[j] = euler[j]/i*(i-1);//先进行除法是为了防止中间数据的溢出     

最近试了一下,发现线筛竟然快了一点?,在2~1000005数据范围下打表,第一种237.7s, 线筛225.8s。。。。

顺带补充一个威尔逊定理:若p为质数,则p能被(p-1)!+1整除。

五.逆元:

对于a*x  = 1 (mod p),我们把x看成a的(数论)倒数,称x为a关于p的逆元。(PS:a和p互质)

a的逆元,我们用inv(a)来表示,那么(a  /  b) % p = (a * inv(b) ) % p = (a % p * inv(b) % p) % p。

逆元的求法:

①用快速幂:已知a^(p-1) ≡ 1 (mod p),两边同时除以a,所以a^(p-2) ≡ inv(a) (mod p),所以inv(a) = a^(p-2) (mod p)。

pow_mod(a, p-2, p);

②用扩展欧几里得算法:已知ax + by = 1,如果a和b互质,则有解,并且这个解的x就是a关于b的逆元,y是b关于a的逆元。

void ex_gcd(LL a, LL b, LL &x, LL &y, LL &d){
    if (!b) {d = a, x = 1, y = 0;}
    else{
        ex_gcd(b, a % b, y, x, d);
        y -= x * (a / b);
    }
}
LL inv(LL t, LL p){//如果不存在,返回-1 
    LL d, x, y;
    ex_gcd(t, p, x, y, d);
    return d == 1 ? (x % p + p) % p : -1;
}

③.当p是个质数的时候有inv(a) = (p - p / a) * inv(p % a) % p. 下面为逆元线性筛的写法

const int mod = 1000000009;  
const int maxn = 10005;  
int inv[maxn];  
inv[1] = 1;  
for(int i = 2; i < 10000; i++)  
    inv[i] = inv[mod % i] * (mod - mod / i) % mod;  

附:求阶乘的逆元

inv[maxn] = mod_pow(fac[maxn], mod-2);  
for(ll i = maxn-1;i >= 0; i--)  
    inv[i] = (inv[i+1] * (i+1)) % mod;  

六.中国剩余定理:

我们先来看一下这个东西:线性同余方程.对于ax ≡ b mod n来说,可以将其转化为一个线性不定方程,即ax-ny = b.

定理:假设方程ax ≡ b mod n有解(即有d|b,d = gcd(a,b)),x0是该方程的任意一个解,则该方程对模n恰有d个不同的解.

分别为 xi = x0 + i*(n/d)  (i = 1,2,..,d-1).

代码实现如下:

int ext_euclid(int a,int b,int &x,int &y)  //求gcd(a,b)=ax+by
{
    int t, d;
    if (b == 0){
		x = 1;
		y = 0;
		return a;
	}
    d = ext_euclid(b,a % b, x, y);
    t = x;
    x = y;
    y = t-a/b*y;
    return d;
}

void modular_equation(int a, int b, int n, int s[])
{
    int e,i,d;
    int x,y;
    d = ext_euclid(a,n,x,y);
    if (b%d>0)
       printf("No answer!\n");
    else{
    	e=(x*(b/d))%n;
        for(i = 0; i < d; i++){
            s[i] = (e + i* (n/d) ) %n; 
        }
}

如果需要对给定的一个单变量模线性方程组(如下式)进行求解,就要利用到中国剩余定理.


中国剩余定理:假设整数m1,m2,.....,mn 两两互质,则对任意的整数:a1,a2,...an,上面的方程组有解。(这是由孙子搞出来的)

可通过如下方式得到:

M = m1 * m2 * ... *mn Mi = M / mi

tiMi ≡ 1 mod mi,通解形式为 x = a1t1M1 + a2t2M2 + ... + antnMn+KM, K∈Z

在模M的意义下,方程组只有一个解。

//n个方程:x=a[i](mod m[i]) (0<=i<n)
LL china(int n, LL *a, LL *m){
    LL M = 1, ret = 0;
    for(int i = 0; i < n; i ++) M *= m[i];
    for(int i = 0; i < n; i ++){
        LL w = M / m[i];
        ret = (ret + w * inv(w, m[i]) * a[i]) % M;
    }
    return (ret + M) % M;
}

如果m1,m2,.....,mn两两不互质呢?,请看如下代码:

typedef pair<LL, LL> PLL;
PLL linear(LL A[], LL B[], LL M[], int n) {	//求解A[i]x = B[i] (mod M[i]),总共n个线性方程组 
    LL x = 0, m = 1;
    for(int i = 0; i < n; i ++) {
        LL a = A[i] * m, b = B[i] - A[i]*x, d = gcd(M[i], a);
        if(b % d != 0)  
			return PLL(0, -1);	//答案不存在,返回-1 
        LL t = b/d * inv(a/d, M[i]/d)%(M[i]/d);
        x = x + m*t;
        m *= M[i]/d;
    }
    x = (x % m + m ) % m;
    return 
		PLL(x, m);//返回的x就是答案,m是最后的lcm值 
}

七.大组合数-卢卡斯定理:

我们不妨先来看看一般的组合数怎么写.  (P可以忽略~)

有一种是比较Low的O(n²)求法,是利用的杨辉三角的性质来写,这里就不贴代码了。

另外一种是利用逆元这个东西,这是一个O(n)的求法,这个也就先不写了。

于是,就到了我们的卢卡斯定理:C(n, m) % p  =  C(n / p, m / p) * C(n%p, m%p) % p

证明:其实整个博客的定理我基本上都不会证明    只有有智慧的人才可以看到...

LL PowMod(LL a, LL b, LL MOD){    
    LL ret=1;    
    while(b){    
        if(b&1) 
			ret = (ret * a) % MOD;    
        a = (a*a) % MOD;    
        b >>= 1;    
    }    
    return ret;    
}    
LL fac[100005];    
LL Get_Fact(LL p)
{    
    fac[0] = 1;    
    for(LL i = 1;i <= p; i++)    
        fac[i] = (fac[i-1] * i) % p;  //预处理阶乘   
}    
LL Lucas(LL n,LL m,LL p)
{    
    LL ret = 1;    
    while(n && m){    
        LL a = n%p, b = m%p;    
        if(a<b) 
			return 0;    
        ret = (ret * fac[a] * PowMod(fac[b] * fac[a-b] % p, p-2, p)) % p;    
        n /= p;    
        m /= p;    
    }    
    return ret;    
}    











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值