通過質因子鏈看數論中的基本問題
阿新 • • 發佈:2022-03-13
例:輸入正整數 X,求 X 的大於 1 的因子組成的滿足任意前一項都能整除後一項的嚴格遞增序列的最大長度,以及滿足最大長度的序列的個數。
輸入格式
輸入包含多組資料,每組資料佔一行,包含一個正整數表示 X。
輸出格式
對於每組資料,輸出序列的最大長度以及滿足最大長度的序列的個數。
每個結果佔一行。
資料範圍
1≤X≤220
輸入樣例:
2
3
4
10
100
輸出樣例:
1 1
1 1
2 1
2 2
4 6
求最大公約數(輾轉相除法)
求公約數本題未用到
int gcd(int a, int b){ //gcd(a, b)表示a和b的最大公約數,0和任何數x的最大公約數都是x本身 return b? gcd(b, a % b) : a; }
*多重集合的排列數問題
算術基本定理 :
通過找最小質因子來得到需要拆分後的數:p -> p / p1 (p1為p的最小質因子) p / p1 -> p / p1/p2(p2為p / p1的最小質因子)
X = p1^α1 * p2^α2 * p3^α3 ... pk^αk
多重集合的排列數問題:
(α1 + α2 + α3 +...+ αk )!/ α1!α2!α3!...αk!
總數全排列,列出所有的情況;再除以相同的數的全排列,去掉相同的情況
本題問題的等價:找出滿足的序列個數 <=> 拆分後的數拼接出最大數的種數, 即問α1個p1、α2個p2...αk個pk有多少種拼接成x的情況
*篩素數-線性篩法
可以在O(N)內求1~n的所有質素,以及每個數的最小質因子
int cnt; int primes[N]; int minp[N]; bool st[N]; int get_primes(int n){ for(int i = 2; i < n; i++){ if(!st[i]){ primes[cnt++] = i; //沒有被篩掉的數就是質數 minp[i] = i; } for(int j = 0; primes[j] * i <= n; j++){ st[primes[j] * i] = true; //可以寫成相乘的形式,不是質數,篩掉 minp[primes[j] * i] = primes[j]; if(i % primes[j] == 0)break; //將i之前的所有質數的i倍數都篩掉,可以防止篩兩遍 } } }
綜合上述幾種演算法本題程式碼為:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = (1 << 20) + 10;
int primes[N];
int cnt;
bool st[N];
int minp[N];
void get_primes(int n){
for(int i = 2; i <= n; i++){
if(!st[i]){
primes[cnt++] = i;
minp[i] = i;
}
for(int j = 0; primes[j] * i <= n; j++){
st[primes[j] * i] = true;
minp[primes[j] * i] = primes[j];
if(i % primes[j] == 0) break;
}
}
}
int main(){
get_primes(N - 1);
int fact[30], sum[N];
int x;
while(scanf("%d", &x) != -1){
int k = 0, tot = 0;
while(x > 1){
int p = minp[x];
fact[k] = p, sum[k] = 0;
while(x % p == 0){
x /= p;
sum[k]++;
tot++;
}
k++;
} //將x劃分成X = p1^α1 * p2^α2 * p3^α3 ... pk^αk的形式,fact存pi、sum存αi
LL res = 1;
for(int i = 1; i <= tot; i++) res *= i;
for(int i = 0; i < k; i++)
for(int j = 1; j <= sum[i]; j++)
res /= j;
printf("%d %lld\n", tot, res);
}
return 0;
}