简单了解了一下数论,有些东西没弄懂,就纪录一下数论的简单知识,以备不时之需
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);//先进行除法是为了防止中间数据的溢出 }