数论
简单了解了一下数论,有些东西没弄懂,就纪录一下数论的简单知识,以备不时之需
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); //先进行除法是为了防止中间数据的溢出 } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步