数论

  简单了解了一下数论,有些东西没弄懂,就纪录一下数论的简单知识,以备不时之需

  1 素数筛法

    (1)埃式筛法

      最简单思路:所有可能的因数全部试一遍。(特判1和2不是素数)

void Init(int n)

{

    memset(book,0,sizeof(book));

    book[0] = 1;

    book[1] = 1;

    for(int i = 2; i <= n; i++)

    {

        if(book[i]==0)

        {

            for(int j = i+i; j <= n; j += i)

            book[j] = 1;
    
        }

    }

}

  (2)欧拉筛法(线性筛法)

本代码保证每个合数只会被它的最小质因数筛去,因此每个数只会被标记一次,所以时间复杂度是O(n)

#include<cstdio>
#include<cstring>
#define MAXN 100005
#define MAXL 1299710
int prime[MAXN];
int check[MAXL];
int tot = 0;
memset(check, 0, sizeof(check));
for (int i = 2; i < MAXL; ++i)
{
    if(!check[i])prime[tot++] = i;
    for (int j = 0; j < tot&&i * prime[j] <= MAXL; ++j){
        check[i*prime[j]] = 1;
        if (i % prime[j] == 0)break;
    }
}

  (3)区间筛素数

思路:因为b以内合数的最小质因数一定不超过sqrt(b),如果有sqrt(b)以内的素数表的话,就可以把筛选法用在[a,b)上了,先分别做好[2,sqrt(b))的表和[a,b)的表,然后从[2,sqrt(b))的表中筛得素数的同时,也将其倍数从[a,b)的表中划去,最后剩下的就是区间[a,b)内的素数了

typedef long long ll;
bool is_prime[maxn];
bool is_prime_small[maxn];
void segment_sieve(ll a,ll b) 
{
     for(ll i=0;i*i<b;++i) is_prime_small[i]=true; //初始化
     for(ll i=0;i<b-a;++i) is_prime[i]=true; //初始化,注意下标变化,为了省空间
     for(ll i=2;i*i<b;++i) {
         if(is_prime_small[i]) {
             for(ll j=2*i;j*j<b;j+=i) is_prime_small[j]=false;  //筛选[2,sqrt(b));
             //(a+i-1)/i得到最接近a的i的倍数,最低是i的2倍,然后筛选
             for(ll j=max(2LL,(a+i-1)/i)*i;j<b;j+=i) is_prime[j-a]=false;
         }
     }
}

2欧几里得

       (1)欧几里得算法 (Euclidean algorithm) ,即大部分选手所知的“辗转相除法”,用来求解最大公约数问题

int gcd(int a, int b)   //欧几里得求最大公约数
{
    if(b == 0) return a;
    return gcd(b, a%b);
}

int lcm(int a,int b){ //最小公倍数
return a/gcd(a,b)*b; //防止溢出
}

  (2)扩展欧几里得

       即ax + by = gcd(a,b);

而当gcd(a,b) = 1,即a,b互质的时候,这个方程的解实际上就对应了a关于模b的逆元。
下面是从欧几里得算法拓展的过程:
首先呢,可以看出:a = gcd(a,b), b = 0 是该式的一个特解;
然后,根据欧几里得算法的原理可以得出:bx + (a%b)y = gcd(a,b);
又因为, a%b = a - (a/b)*b (a/b 为整除关系)
所以原式化为: bx + (a - (a/b)*b)y = gcd(a,b);
整理得: ay + b * (x - (a/b) * y) = gcd(a,b);
所以解之间的递归关系为:
- xx = y;
- yy = x - (a/b)y;

int extend_Euclid(int a, int b, int &x, int &y)
{
    if(b==0)
    {
        x = 1;
        y = 0;
        return a;
    }
    int r = extend_Euclid(b, a%b, y, x);
    y -= a/b*x; //这里已经是递归,回溯的过程了,x,y已经颠倒了
    return r;
}

  

    同时,可以给出通解的方程:

   x = x0 + b/gcd(a,b)*t;

   y = y0 - a/gcd(a,b)*t;

   (x0, y0 为方程的一组特解, t为整数)

三.模与余:
(1)模运算:
1.取模运算:a % p(a mod p),表示a除以p的余数。
2.模p加法:(a + b) % p = (a%p + b%p) % p
3.模p减法:(a - b) % p = (a%p - b%p) % p
4.模p乘法:(a * b) % p = ((a % p)*(b % p)) % p
5.幂模p : (a^b) % p = ((a % p)^b) % p
6.模运算满足结合律、交换律和分配律。
7.a≡b (mod n) 表示a和b模n同余,即a和b除以n的余数相等。

(2)同余的性质:
1.反身性:a≡a (mod m);
2.对称性:若a≡b(mod m),则b≡a (mod m);
3.传递性:若a≡b(mod m),b≡c(mod m),则a≡c(mod m);
4.同余式相加:若a≡b(mod m),c≡d(mod m),则a c≡b d(mod m);
5.同余式相乘:若a≡b(mod m),c≡d(mod m),则ac≡bd(mod m)。
(3)快速幂

//快速幂求a^b; 
#include<bits/stdc++.h>
using namespace std;
int k(int a,int b){
	int t = 1;
	while(b){
		if(b%2 != 0){
			t *= a;
			b--;
		}
		a *= a;
		b /= 2;
	}
	return t;
}
int main()
{
	int a,b;
	scanf("%d%d",&a,&b);
	int x = k(a,b);
	cout<<x<<endl;
	return 0; 
}



/*//快速幂求a^b的后y位; 
#include<bits/stdc++.h>
using namespace std;
int k(int a,int b,int y){
	int t = 1;
	while(b){
		if(b%2 != 0){
			t = (a*t)%y;
			b--;
		}
		a = (a*a)%y;
		b /= 2;
	}
	return t;
}
int main()
{
	int a,b,y;
	scanf("%d%d%d",&a,&b,&y);
	int x = k(a,b,y);
	cout<<x<<endl;
}*/

  四.唯一分解定理

            题目链接:http://lightoj.com/login_main.php?url=volume_showproblem.php?problem=1341

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1000050;
typedef long long ll;
ll a[N],b[N],k = 0;
ll init(ll n){
	ll s = 1;
	if(n == 0){
		return 0;
	}
	ll tt = 0;
	ll i = 0;
	while(a[i] < n && i < k){
		tt = 0;
		if(n % a[i] == 0){
			while(n % a[i] == 0){
				n /= a[i];
				tt++;
			}
		}
		s *= tt+1;
		i++; 
	}
	if(n > 1){
		s *= 1+1;
	}
	return s;
}
int main()
{
	k = 0;
	ll i = 0;
	for( i = 2; i <= N; i++){
		if(!b[i]){
			a[k++] = i;
			for(ll j = i+i; j <= N; j += i){
				b[j] = 1;
			}
		}
	}
	int t;
	scanf("%d",&t);
	int h = 0;
	while(t--){
		h++;
		ll n,m,num = 0,cns = 0;
		scanf("%lld%lld",&n,&m);
		if(m >= sqrt(n)){
			num = 0;
		}
		else{
			for(i = 1; i < m; i++){
				if(n%i == 0){
					cns++;
				}
			}
			num = init(n)/2;
			num -= cns;
		}
		printf("Case %d: %lld
",h,num);
	}
	return 0;
}

  

五.欧拉函数:
5.1定义:
互质:gcd(a,b)=1,则a,b互质
欧拉函数φ(n):小于等于n的所有数中与n互质数的个数

5.2性质:
1.对于质数p, φ( p) = p - 1。注意φ(1)=1
2. 若m,n互质,φ(mn)=φ(m)φ(n)
3. 当n为奇数时,φ(2n)=φ(n)
4.当n大于2时,所有φ(n)都是偶数
5.当n大于6时,所有φ(n)都是合数

6.欧拉定理:对于互质的正整数a和n,有a^φ(n) ≡ 1 mod n
7.费马小定理:若gcd(a,b)=1,则a^(p-1)=1 mod n
8.欧拉定理推论:小于等于n的数中,与n互质数的总和为:φ(n)*n/2 (n>1)

//求一个数的欧拉函数
//直接求解欧拉函数 int euler(int n){ //返回euler(n) int res=n,a=n; for(int i=2;i*i<=a;i++){ if(a%i==0){ res=res/i*(i-1);//先进行除法是为了防止中间数据的溢出 while(a%i==0) a/=i; } } if(a>1) res=res/a*(a-1); return res; }

  

//求1-n每个数的欧拉函数
void Init(){ 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);//先进行除法是为了防止中间数据的溢出 }

  

原文地址:https://www.cnblogs.com/clb123/p/10992792.html