数论

 

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

  1 素数筛法

    (1)埃式筛法

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#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)内的素数了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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) ,即大部分选手所知的“辗转相除法”,用来求解最大公约数问题

1
2
3
4
5
int gcd(int a, int b)   //欧几里得求最大公约数
{
    if(b == 0) return a;
    return gcd(b, a%b);
}<br><br>int lcm(int a,int b){  //最小公倍数<br>    return  a/gcd(a,b)*b;  //防止溢出 <br>}

  (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;

1
2
3
4
5
6
7
8
9
10
11
12
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)快速幂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//快速幂求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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#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\n",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)

1
2
3
4
5
6
7
8
9
10
11
12
//求一个数的欧拉函数<br>//直接求解欧拉函数 
  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
2
3
4
5
6
7
8
9
//求1-n每个数的欧拉函数<br>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);//先进行除法是为了防止中间数据的溢出
}

  

posted @   陈墨cacm  阅读(611)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示