<style> .reveal .slides { text-align: left; font-size:30px } </style> # Math --- * Miller Rabin * Pollard Rho * DiscreteSqrt * FFT * Josephus Queries * 歐拉函數 * $a^{b^c}$ --- 先備知識 : 若是數字太大,但是在 $-2^{127}$ ~ $2^{127}$ 的範圍,可以轉為使用 \_\_int128 來進行計算 而若輸出輸入需要,則有兩種處理方式 1. 快讀快出(推薦) 2. 字串處理 --- ## Miller Rabin ---- ## [模板](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/math/Miller_Rabin.cpp) 大家應該聽過這個算法很多次了,那他其實是一個隨機化的質數判定算法,而為何他會特別的出眾呢?因為他的複雜度只需要 $O(k log^3 n)$ -- $k$ 為底數數量 而他的引入需要透過兩個定理,分別是費馬小定理以及二次探索定理,那不重要,所以不講。只需要知道怎麼用就好。 ---- ## 用法 那因為他是一個隨機化的算法,所以它並不是百分百正確,而是機率正確,那錯誤要怎麼改正呢? 就是取決於你取用的底數,對於這個算法來說你選取誰去做底數是非常重要的 1. 在int範圍內 選取 $\{1,7,61\}$ 會使得這個算法百分百正確 2. 在以上的範圍,則寫在模板裡面,可以直接看模板用。 就是每次拿底數去跟 $n$ 做一些探索,使得可以快速分辨他是否為質數。 回傳 $Yes/ No$ 是否為質數。 ---- ## 用法 需要處理或是引入的函數 : $mul$ , 把底數寫進 magic[] 陣列裡面 , 以及定義 $s$ 的大小 ```cpp int c; cin>>c; if(miller_rabin(c)){ cout<<"prime\n"; } else{ cout<<"not_prime\n"; } ``` --- ## Pollard Rho ---- ## [模板](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/math/pollardRho.cpp) 他的作用是,可以快速地找到一個整數的其中一個因數(所以質數不能用),而核心也是隨機化算法。 複雜度為 $O(n^{( \frac 14)})$ 回傳一個整數為傳入 $n$ 的因數。 ---- 什麼題目都用的到,可以配合Miller Rabin一起使用,做到質因數在$\log n$時間內的分解。 ---- ## 用法 需要處理或是引入的函數 : $add$ , $mul$ ```cpp int c; cin>>c; cout<<pollard_rho(c); ``` Miller Rabin 和 pollard_rho 常常因為複雜度的關係會搭配著使用,是在許多高難度的數論題目都會用到的常見模板。 其中 add(x, y, p) 為 (x+y)%p mul(x, y, p) 為 (x*y)%p 要記得 $x+y$, $x\cdot y$ 會超過 long long,請使用 \_\_int128 計算 ---- ## 利用Pollard Rho找到所有的質因數 ```cpp #define i64 __int64 vector<i64> ret; void fact(i64 x) { if (miller_rabin(x)) { ret.push_back(x); return; } i64 f = pollard_rho(x); fact(f); fact(x/f); } ``` 對。直接加這個。 你的 $ret$ 就會是所有的質因數 而 $vector$ 去 sort 或是做任何操作就可以找到你想找的數字。 ---- ## [例題](https://www.luogu.com.cn/problem/P4718) 題意 : 詢問一個數字的最大質因數,若最大質因數為他本身,則直接輸出 "Prime" 題解 : 那既然我們有了上面的工具,我們就只要先使用 Miller Rabin 判斷他是否為質數,然後再使用 Pollard Rho 搭配 fact 去找出所有質數,然後 sort 找出最大的即可。 --- ## Discrete_Sqrt ---- ## [模板](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/math/DiscreteSqrt.cpp) 解決 $x^2 \mod p = a$ 的算式問題,根據模板我們可以看到, $a$ 和 $p$ 就是傳進去的 $a$ 和 $p$ ,而 $x$ 和 $y$ 則是分別的兩種答案,因為會有 $x$ 和 $-(x)$ 兩種答案 這個模板的複雜度並沒有很重要,他就是專門拿來解決這個問題的通解,有遇到這種問題就砸這個 穩。 ---- ## [例題](https://www.acmicpc.net/problem/17603) 題意 : $t$ 筆測資,分別給出 $p , a_0 , a_1$ 求 $b_0 , b_1$,條件如下 : 1. $(b_0 \cdot b_1) \mod p = a_0$ 2. $(b_0 + b_1) \mod p = a_1$ 按照升序排列輸出 $b_0 , b_1$ 若沒有符合條件的答案則輸出 $-1 題解 : $\sqrt{(b_0 + b_1)^2 - 4 \cdot (b_0 \cdot b_1)}$ --- ## FFT ---- ## FFT 快速傅立葉變換 ## [模板](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/math/FFT.cpp) 主要說白了就是很快的多項式乘法,給多項式 A 和 B 求出兩個多項式的乘積。 其中FFT算是數論裡愛出的題目,像是ICPC或者是之前校內賽的海洋盃都有出現,且很容易在各大賽場被重複包裝,算是數論的常客。 時間複雜度為 $O(n \log n)$ 原本樸素的暴力做法為 $O(n^2)$ 是有一定量級上的差距的 ---- ## 用法 在做所有事情之前,需要先跑一次 $pre\_fft$,而傳入的 $mul()$ 分別為 - 第一個多項式的長度 $\_n$, 第一個多項式 $a[\ ]$ - 第二個多項式的長度 $\_m$, 第二個多項式 $b[\ ]$ - 要儲存答案的陣列 $ans[\ ]$ 從頭到尾都不需要跑 $fft()$ 這個函式,讓他自己處理就好 記得 MXN 要設成 2 的冪次,設 $\ge$ 計算的長度的 2 的冪次 假設題目 n 最大為 10 ,乘法後會到 2*n,那 MXN 就要設成 32 (16太小) ---- ## [例題 1 :apple: and :banana: ](https://cses.fi/problemset/task/2111) 此題為 CSES 上的 FFT 入門 題意 : 你有 $n$ 個蘋果和 $m$ 個香蕉,他們的重量介於 $1$ 到 $k$ 之間,今天請你計算對於每個介於 $2$ ~ $2k$ 之間的重量 $w$ ,有幾種方式可以挑選一個蘋果和香蕉,使他們重量總重為$w$。(每顆重量最重為20000) 題解 : 那這題的重點是 $1 \leq a_i , b_i \leq 20000$ ,所以可以直接把重量變成次方,然後進行多項式乘法,就相當於次方的加法,最後輸出 $k$ ~ $2k$ 的次方的值即可。 ---- ## [例題 2 一個bit的位置](https://cses.fi/problemset/task/2112) 此題為CSES上的FFT入門 題意 : 給定一個二進制長度為 $n$ 的字串,計算每個為 $1$ 的 bit 的位置下標的差值,並在最後輸出。共有 $1$ ~ $n-1$ 個。 題解 : 你會發現,這題是要算差值,所以我們先把它改成次方丟進去,然後把其中一邊全部 $*(-1)$ 接下來跑去做FFT就會對了。 --- ## Josephus Queries ---- ## 約瑟夫問題 ## [模板](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/math/Josephus.cpp) 應該是一個很常見的問題,其問題的複雜度也有非常非常多種,在這幾年也有各式各樣的進步,各式各樣的寫法,但依然沒有一種被公認最好的寫法或是最好的複雜度 而模板應該非常非常簡單,就是 $n$ 個人每 $m$ 次pick一個人,返回最後剩下來的人是誰。 ~~順帶一提 除非你要找出位置,否則請不要用linked list 去實作 很蠢~~ 而其中若是要求被踢出去的位置,會另外有非常多種寫法,就看每個人怎麼寫 ---- ## 線性時間內 它的核心就是,每次出局一個人,剩下 $n-1$ 個人,然後再加上一個相對位移 $k$ 去得到正確的答案,而此模板算法很明顯是 $O(n)$ ---- ## 對數 此種算法的核心是把 $n$ 壓掉,把問題面著重在每 $k$ 個人就有一個要get out的情況下的遞迴。時間複雜度為 $O(k \log n)$ ```cpp int josephus(int n, int k) { if (n == 1) return 0; if (k == 1) return n - 1; if (k > n) return (josephus(n - 1, k) + k) % n; int res = josephus(n - n / k, k); res -= n % k; if (res < 0) res += n; else res += res / (k - 1); return res; ``` ---- ## 例題 沒有例題,就是給大家知道一下 若題目詢問 第 $k$ 次出局的人是誰 ```cpp int ja(int N,int M,int i) { if(i==1) { return (M-1+N)%N; } return (ysfdg(N-1,M,i-1)+M)%N; } ``` 0-base 輸出離開隊列的順序 --- ## 歐拉函數 ---- ## 定義 $\phi (x)$ 為小於等於 $x$ 且和 $x$ 互質的數字個數,也就是 $\phi (1)=1$ 若 $x$ 為質數 則 $\phi (x)=x-1$ ---- ## 性質 (看過 有印象就好 1. 歐拉函數為積性函數 \*積性函數是指 若有 $gcd(a,b)=1$ 那 $\phi (a*b)=\phi(a)*\phi(b)$ 其中 若 $x$為奇數時,$\phi(2x)=\phi(x)$ 2. $n=\sum_{d \mid n} \phi(d)$ 3. 若$n=p^k$ ,且 $p$ 是質數的話,那 $\phi(n)=p^k-p^{k-1}$ 4. 由算術基本定理可知 $\phi(n)=n*\prod_{i=1}^s \frac {p_i-1}{p_i}$ 由第四點求出的通式$\phi(n)=x*\prod_{i=1}^n (1-\frac 1{p_i}))$ ---- 怎麼求歐拉函數 ```cpp int Euler(int n){ int now = n; for (int i = 2; i * i <= n; i++) if (n % i == 0){ now = now - now / i; while (n % i == 0) n = n / i; } if (n > 1) now = now - now / n; return now; } ``` ---- 那當然上面那個看起來很慢,所以我們會有更快的 : ```cpp int prime[10010], phi[10010]; bool v[10010]; void quick_euler(){ int cnt = 0; for(int i = 2; i <= N; ++i){ if(!v[i]) prime[++cnt] = i, phi[i] = i - 1; // 若 i 是質數,所以 φ(i) = i - 1 for(int j = 1; i * prime[j] <= N && j <= cnt; ++j){ v[i * prime[j]] = 1; if(i % prime[j] == 0){ phi[i * prime[j]] = phi[i] * prime[j]; break; } else phi[i * prime[j]] = phi[i] * (prime[j] - 1); } } } ``` --- ## $a^{b^c}$ 這個有兩件事情值得講的 1. $(a^{b^c})(\mod m)$ 2. $a^{(b^c)}(\mod m)$ ---- ## $(a^{b^c})(\mod m)$ 好,首先這件事情,會想起於離散數學時學到的東西 $a^m \% m = 0$ $a^{m-1} \% m = 1$ $a^{m-2} \% m = a^{-1}$ 於是會發現這件事情 $a^b ≡ a^{(b \mod {m−1})} (\mod m)$ 所以同理可以推到C,於是就可以直接在紙上解開。 ---- 那如果今天遇到 $a^{b^c}(\mod m)$ 時要怎麼辦呢 ---- *歐拉函數 ${\displaystyle a^{\varphi (m)}\equiv 1{\pmod {m}}.}$ 首先,根據歐拉定理以及其函數的應用,可以知道 $A^{(B^C)} (\mod m) == A^{(B^C \mod (\phi(m) — 1))}(\mod m)$ 然後又因 $m$ 通常是一個大的質數,而若大家有記憶的話,歐拉函數時有提到,若 $m$為質數,則 $\phi(m)=m-1$ 於是又可以得知 $A^{(B^C \mod (m — 2))}(\mod m)$ 所以最後 $a^{b^c}(\mod m) = A^{(B^C \mod (m — 2))}(\mod m)$ --- 作業;
{"title":"math","contributors":"[{\"id\":\"e4a3af8b-860c-46a0-96f1-558667fdcf41\",\"add\":8474,\"del\":2033},{\"id\":\"19f09ccf-6b99-452f-971f-955cfc1657f3\",\"add\":235,\"del\":249}]"}
    574 views
   Owned this note