屏下攝像頭在望:蘋果已為 iPhone 13/Pro 測試屏下指紋技術,而目標是屏下 Face ID
質數
線性篩法
#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 1e6 + 10; int n, cnt; int st[N]; void n_prime(int n) { for (int i = 2; i <= n; i ++) { if (!st[i]) { prime[cnt++] = i; } for (int j = 0; prime[j] <= n / i; j ++) { st[prime[j] * i] = 1; if (i % prime[j] == 0) break; } } } int main () { cin >> n; e_prime(n); cout << cnt; return 0; }
埃氏篩法
#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 1e6 + 10; int n, cnt; int st[N]; void e_prime(int n) { for (int i = 2; i <= n; i ++) { if (st[i]) continue; prime[cnt++] = i; for (int j = i + i; j <= n; j += i) st[j] = 1; } } int main () { cin >> n; e_prime(n); cout << cnt; return 0; }
求組合數
求組合數
首先給出組合數的定義:\(C\_n^m = \frac{\overbrace {n \times (n - 1) \times (n - 2) \times ...\times (n - m + 1)}^\text{m 個 數}}{1 \times 2\times 3\times ... \times m} = \frac{n!}{(n - m)! \times m!} = C\_{n -1}^{m} + C\_{n - 1}^{m - 1} \tag {1}\)
對於 $ 1 $ 號公式,我們可以這樣理解:假如我們有 n 個蘋果,要從中任意挑選 m 個蘋果,我們首先可以拿出來 1 個蘋果,那麼接下來一共就兩種方案:
- 包含這 1 個蘋果的方案 : 也就是從 n - 1個蘋果中挑選 m - 1 個蘋果。
\(C\_{n - 1}^{m - 1} \tag {*}\)
- 不包含這 1 個蘋果的方案,也就是從 n - 1 蘋果中挑選 m 個蘋果。
\(C\_{n - 1}^{m} \tag {**}\)
由以上公式我們就得到如下遞推式:
\(C\_n^m = C\_{n -1}^{m} + C\_{n - 1}^{m - 1} \tag {***}\)
遞推式計算的時間複雜度是 \(O(n^2)\) 的, 其中 \(n\) 代表 \(max(a, b)\) 。
很像後面動態規劃的整數劃分那個題目。
此外還有一種 \(O(nlog_n)\) 的計算方法。
需要用到快速冪求逆元跟預處理階乘。
\(C\_n^m =\frac{n!}{(n - m)! \times m!}\)
逆元又是什麼呢?之前基礎課講過,這裡簡單的再介紹一下。
比如要求一個 \(\frac{a}{b}~ \% ~mod\) ,因為對於除法的取餘我們很難計算,因為有時候並不是整除,而且最重要的是
\(\frac{a}{b} ~\%~ mod \neq \frac{a~ \%~ mod}{b ~\%~ mod}\) ,因此我們轉換成逆元計算:
設 \(x\) 為 \(b\) 的逆元,那麼 \(\frac{a}{b} ~\%~ mod \equiv (a ~\%~ mod) \times (x ~\%~mod) \% ~mod\)
計算逆元的方法:\(X = Quickpower(B, Mod - 2, Mod)\),當且僅當 \(Mod\) 是一個質數的時候成立。
時間複雜度:\(O(n + log_{mod})\)。
\(Lucas~Theory\) 盧卡斯定理 \(O(p \times log\_N \times log\_p)\) ,其中 \(p\) 為模數且必須是質數,\(N\)為\(ab\)的大小.
證明略。
\(C_{a}^{b} \equiv C_{a~mod~p}^{b~mod~p} \times C_{\frac{a}{p}}^{\frac{b}{p}} ~~ (mod~p)\)
\(O(n^2)\) 遞推公式求組合數
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2010, MO = 1e9 + 7;
int c[N][N];
void init () {
for (int a = 0; a < N; a ++) {
for (int b= 0; b <= a; b ++) {
if (b == 0) c[a][b] = 1;
else c[a][b] = (c[a - 1][b] + c[a - 1][b - 1]) % MO;
}
}
}
int main () {
int n;
cin >> n;
init();
while (n --) {
int a, b;
scanf("%d %d", &a, &b);
printf("%d\n", c[a][b]);
}
return 0;
}
\(O(n~log_{n})\) 逆元做法
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 100010, mod = 1000000007;
int fact[N], infact[N];
int n;
int qmid(int a, int b, int p) {
int res = 1;
while (b) {
if (b & 1) res = (LL) res * a % p;
a = (LL) a * a % mod;
b >>= 1;
}
return res;
}
int main () {
cin >> n;
fact[0] = infact[0] = 1;
for (int i = 1; i < N; i ++) {
fact[i] = (LL)fact[i - 1] * i % mod;
infact[i] = (LL)infact[i - 1] * qmid(i, mod - 2, mod) % mod;
}
while (n --) {
int a, b;
cin >> a >> b;
printf("%d\n", (LL)fact[a] * infact[a - b] % mod * infact[b] % mod);
}
return 0;
}
\(O(p \times log_N \times log_p)\)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int p;
int qmid(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = (LL) res * a % p;
a = (LL) a * a % p;
b >>= 1;
}
return res;
}
int C(int a, int b) {
int res = 1;
for (int i = a, j = b; j >= 1; j --, i --) {
res = (LL) res * i % p;
res = (LL) res * qmid(j, p - 2) % p;
}
return res;
}
int locas (LL a, LL b) {
if (a < p && b < p) return C(a, b) % p;
return (LL)C(a % p, b % p) * locas(a/p, b/p) % p;
}
int main () {
int n;
cin >> n;
while (n --) {
LL a, b;
cin >> a >> b >> p;
cout << locas(a, b) << endl;
}
return 0;
}
y總的程式碼
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 5010;
int primes[N], cnt;
int sum[N];
bool st[N];
void get_primes(int n){
for (int i = 2; i <= n; i ++ ){
if (!st[i]) primes[cnt ++ ] = i;
for (int j = 0; primes[j] <= n / i; j ++ ){
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
int get(int n, int p){
int res = 0;
while (n){
res += n / p;
n /= p;
}
return res;
}
vector<int> mul(vector<int> a, int b){
vector<int> c;
int t = 0;
for (int i = 0; i < a.size(); i ++ ){
t += a[i] * b;
c.push_back(t % 10);
t /= 10;
}
while (t){
c.push_back(t % 10);
t /= 10;
}
return c;
}
int main(){
int a, b;
cin >> a >> b;
get_primes(a);
for (int i = 0; i < cnt; i ++ ){
int p = primes[i];
sum[i] = get(a, p) - get(a - b, p) - get(b, p);
}
vector<int> res;
res.push_back(1);
for (int i = 0; i < cnt; i ++ )
for (int j = 0; j < sum[i]; j ++ )
res = mul(res, primes[i]);
for (int i = res.size() - 1; i >= 0; i -- ) printf("%d", res[i]);
puts("");
return 0;
}