1. 程式人生 > 資訊 >屏下攝像頭在望:蘋果已為 iPhone 13/Pro 測試屏下指紋技術,而目標是屏下 Face ID

屏下攝像頭在望:蘋果已為 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. 包含這 1 個蘋果的方案 : 也就是從 n - 1個蘋果中挑選 m - 1 個蘋果。

\(C\_{n - 1}^{m - 1} \tag {*}\)

  1. 不包含這 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;
}