质因子分解
唯一分解定理
任何数都可由素因子之积构成
- N = p 1 a 1 × p 2 a 2 × p 3 a 3 … … p n a n N=p_1^{a_1} \times p_2^{a_2} \times p_3^{a_3}……p_n^{a_n} N=p1a1×p2a2×p3a3……pnan ( p i p_i pi 为素因子)
方法1:暴力分解
枚举因子,并暴力分解,时间复杂度: O ( n 1 / 2 ) O(n^{1/2}) O(n1/2)
int p[100],a[100],cnt;
void divide(int n){
int ans=0;
for(int i=2;i*i<=n;i++){
if(n%i==0){
p[++cnt]=i;
while(n%i==0){
n/=i;
a[cnt]++;
}
}
}
if(n>1){
p[++cnt]=n;
a[cnt]=1;
}
for(int i=1;i<=cnt;i++)cout<<p[i]<<" "<<a[i]<<endl;
}
方法2:预处理一个 nxt
数组再分解
使用埃筛或欧拉筛,预处理出 nxt
数组,其中 nxt[i]
表示 i
的最小质因子。预处理出 nxt
数组之后,对每个数质因子分解的时间复杂度为:
O
(
l
o
g
)
O(log)
O(log) 。总时间复杂度:
O
(
n
+
T
l
o
g
n
)
O(n+Tlogn)
O(n+Tlogn) 。
//埃筛,时间复杂度:O(nloglogn),大约2e6适用
void init() {
for(long long i=2; i<=N; i++) {
if(nxt[i]!=0)continue;
nxt[i]=i;
for(long long j=i*i; j<=N; j+=i) {
if(nxt[j]==0)nxt[j]=i;
}
}
}
//欧拉筛,时间复杂度:O(n),大约2e7适用
void init(){
}
void div_it(int x) {
while(x!=1) {
int add=0,div=next[x];
while(next[x]==div) {
add++;
x=x/div;
}
a.push_back(make_pair(div,add));
}
//保证div是从小到大的。
}
判断质数
方法1:试除法
原理:判断 n 1 / 2 n^{1/2} n1/2 之内是否有因子即可。
int isPrime(int x){
if(x<2)return 0;
int t=sqrt(x);
for(int i=2;i<=t;i++){
if(x%i==0)return 0;
}
return 1;
}
方法2:埃筛
原理:对于如何一个非 1 1 1 的正整数,其大于 1 的整数倍,均不可能是质数。时间复杂度: O ( n l o g l o g n ) O(nloglogn) O(nloglogn)
【补充】时间复杂度较欧拉筛高,但功能性强,可以处理出的信息更多。
const int N=2e6+10,R=2e6;
int prime[N],cnt;//素数表
int p[N];//判断素数
void init(){
p[0]=p[1]=1;
for(int i=2;i<=N;i++){
if(p[i]==1)continue;
prime[++cnt]=i;
for(int j=2*i;j<=N;j+=i)p[j]=1;
}
}
方法3:欧拉筛
原理:与埃筛类似。唯一的优点是:快 。时间复杂度: O ( n ) O(n) O(n)
const int N=2e6+10,R=2e6;
int prime[N],cnt;//素数表
int p[N];//判断素数
void init(){
p[0]=p[1]=1;//0则素数,1则非素数
for(int i=2;i<=N;i++){
if(p[i]==0)prime[++cnt]=i;//判断 i是否为素质
for(int j=1;j<=cnt;j++){//每个已知素数的 i倍都会被标记
if(i*prime[j]>N)break;//范围越界就break
p[i*prime[j]]=1;
if(i%prime[j]==0)break;//保证了每个数只会被标记一次
}
}
}
例题练习:
问题1:因子个数
方法1:质因子分解
a n s = ∏ ( k i + 1 ) ans=\prod (k_i+1) ans=∏(ki+1) 。其中 k i k_i ki 是每个质因子的指数。
O ( n + T l o g n ) O(n+Tlogn) O(n+Tlogn) 即可求出 T T T 个数的因子个数。
方法2:调和级数
对于每个数 i i i,枚举其整数数字 j j j ,显然 j j j 有 i i i 这个因子。
O ( n l o g n ) O(nlogn) O(nlogn) 即可求出 [ 1 − n ] [1-n] [1−n] 内每个数的因子个数。
该方法甚至可以求出每个数的因子集合。
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j+=i){
f[j].push_back(i);
}
}
问题2:求 [1,n] 中所有数的因子个数一共有多少个
方法:整除分块
如果用调和级数求,需要 n l o g n nlogn nlogn 求。
枚举因子 i i i , [ 1 , N ] [1,N] [1,N] 中一共有 [ N / i ] [N/i] [N/i] 个数有 i i i 这个因子。
因此累加就是: O ( ∑ i = 1 n [ n / i ] ) O(\sum_{i=1}^n [n/i]) O(∑i=1n[n/i])