AcWing 197. 階乘分解
阿新 • • 發佈:2022-05-18
一、排除錯誤作法
錯誤作法I
直接算出\(N\)的階乘,資料範圍\(1e6\),階乘太大\(long \ \ long\)裝不下,就開高精度,然後再考慮質因子分解,這麼麻煩就等著掛吧,而且,高精度後,也不知道咋能進行質數因子分解,反正我是不會...
錯誤作法II
把\(1~N\)中每一個數字都質因數分解,分別計算質因子個數,然後用桶計數,累加!
這樣做的演算法複雜度是多少呢?
\(O(N\sqrt{N})\),其中\(N=10^6\),那就是\(10^6 * 10^3=10^9\),這麼幹會\(TLE\)了!
二、經典演算法分析
三、演算法步驟
- 篩出\(1 \sim n\)的所有質數
- 列舉每個質因子\(x\),\(n!\)表示\(1 * 2 * 3... * n\),從\(1\)到\(n\)中,求\(x\)的次數:
(直到\(x\)的次方大於\(n\)停止)
四、時間複雜度 \(O(n)\)
質數個數定理:\(1 \sim n\)中有大約有 $ \frac{n}{ln(n)}$ 個質數。
有\(\frac{n}{ln(n)}\)
總的計算次數=$$ \frac{n}{ln(n)} * log_xn <= \frac{n}{log_2n} * log_2n =n$$
使用數字\(2\)進行換底,將等式值擴大,最大是\(n\),因此時間複雜度是\(O(n)\)級別
五、實現程式碼
#include <bits/stdc++.h> using namespace std; typedef long long LL; //尤拉篩 const int N = 1e6 + 10; int primes[N], cnt; // primes[]儲存所有素數 bool st[N]; // st[x]儲存x是否被篩掉 void get_primes(int n) { memset(st, 0, sizeof st); cnt = 0; for (int i = 2; i <= n; i++) { if (!st[i]) primes[cnt++] = i; for (int j = 0; primes[j] * i <= n; j++) { st[primes[j] * i] = true; if (i % primes[j] == 0) break; } } } int main() { int n; cin >> n; get_primes(n); for (int i = 0; i < cnt; i++) { int p = primes[i]; int s = 0; // 方法1(yxc大佬思維): // 思路:由大到小+除法降維 // 優點:不用考慮乘法而導致的爆int上限 // 缺點:逆向思維,不好想 for (int j = n; j; j /= p) s += j / p; // 方法2(普通人正常思維) // 思路:由小到大+累記乘方 // 優點:正向思維 // 缺點:因為存在極限的乘法,可能爆int,需要開LL變數 // for (LL j = p; j <= n; j *= p) s += n / j; // 方法3(錯誤作法) // 因為最後 j*=primes[i]會造成j爆int,變成負數,導致s變小) // for (int j = p; j <= n; j *= p) s += n / j; printf("%d %d\n", p, s); } return 0; }