<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 來進行計算 --- ## 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$ , 把底數寫進魔法數字裡面 , 以及定義 $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$ 的因數。 ---- 我也不知道要說甚麼。反正他什麼題目都用的到拉 ---- ## 用法 需要處理或是引入的函數 : $add$ , $mul$ , $\_\_gcd$ ```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 或者使用 [O(1) mul](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/math/O(1)mul.cpp) ---- ## 利用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$ 去做一些操作就可以找到最大的 ---- ## [例題](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)}$ --- ## 高斯消 ---- ## [模板](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/math/gauss.cpp) 高斯消元法,應該大家都有在高中的時候學過,其中值得一提的是,當其用來解 xor方程組的時候,可以使用bitset優化。 時間複雜度為 $O(n^3)$ 那我們先教模板的用法(當然還有浮點數的用法),在教更進階的轉換方式。 ---- ## 用法 需要處理或是引入的函數 : $ppow$ 快速冪 大家應該都記得,在解出 $x$ 個未知數的多項式時,就會需要 $x$ 條式子,而他就可以開出一個 $x *x$ 的增廣矩陣。 其中模板裡面的 ```cpp gs.v.resize(n , vector<int>(n + 1 , 0)); ``` 就是要記錄每一行的增廣矩陣。 而 $ans$ 的vector就是紀錄其中每個未知數依序的答案 就像是模板上寫的一樣,需要先 clear 然後再 resize x 的大小,就可以代入使用。 ---- ## xor in gauss 異或的方程組會長得像是下面這樣 ![](https://hackmd.io/_uploads/H1ceFFij3.png) 一樣就只需要把參數記錄下來,直接跑就可以 而其中時間複雜度會降低成 $O(\frac {n^2m}{w})$ 但其實沒甚麼差。算過之後就會發現他只降低了一點點。 ```cpp bitset<1005> vector[n+1]; ``` ---- 理論上會長得像是這樣 ```cpp bitset<1010> matrix[2010]; //增廣矩陣 vector<bool> Gauss(int n, int m) { for (int i = 1; i <= n; i++) { int cur = i; while (cur <= m && !matrix[cur].test(i)) cur++; if (cur > m) return std::vector<bool>(0); if (cur != i) swap(matrix[cur], matrix[i]); for (int j = 1; j <= m; j++) if (i != j && matrix[j].test(i)) matrix[j] ^= matrix[i]; } std::vector<bool> ans(n + 1, 0); for (int i = 1; i <= n; i++) ans[i] = matrix[i].test(0); return ans; } ``` --- ## FFT ---- ## FFT 快速傅立葉變換 ## [模板](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/math/FFT.cpp) 主要說白了就是很快的多項式乘法,根據模板上面寫的呢,我們只需要把第一個多項式填入 $a[\ ]$ 第二個多項式填入 $b[\ ]$ ,然後得出的 $ans[\ ]$ 就是乘法的答案。 其中FFT算是本日比較常用到的東西(應該是辣),像是ICPC或者是之前校內賽的海洋盃都有出現,本週的作業其中一題就是海洋盃曾出過的題目。 時間複雜度為 $O(n lg n)$ 原本樸素的暴力做法為 $O(n^2)$ 是有一定量級上的差距的 ---- ## 用法 需要處理或是引入的函數 : 只有MAXN 在做所有事情之前,需要先跑一次 $pre_fft$,而傳入的 $mul()$ 分別為 第一個多項式的長度,第一個多項式,第二個多項式的長度,第二個多項式,答案陣列. 從頭到尾都不需要跑 $fft()$ 這個函式,讓他自己處理就好 ---- ## [例題 1 :apple: and :banana: ](https://cses.fi/problemset/task/2111) 此題為CSES上的FFT入門 題意 : 你有 $n$ 個蘋果和 $m$ 個香蕉,他們的重量介於 $1$ 到 $k$ 之間,今天請你計算對於每個介於 $2$ ~ $2k$ 之間的重量 $w$ ,有幾種方式可以挑選一個蘋果和香蕉,使他們重量總重為$w$。 題解 : 那這題的重點是 $1 \leq a_i , b_i \leq 100$ ,所以可以直接把重量變成次方,然後進行多項式乘法,就相當於次方的加法,最後輸出 $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 lg 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 輸出離開隊列的順序 --- ## $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)$ 那大家稍微動個腦應該就能直接解開了 ---- 那如果今天遇到 $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)$ --- ## [HW](https://vjudge.net/contest/572863) ## 可能可以...寫出 $\frac 23$ 喔...
{"title":"Math","contributors":"[{\"id\":\"19f09ccf-6b99-452f-971f-955cfc1657f3\",\"add\":212,\"del\":6},{\"id\":\"e4a3af8b-860c-46a0-96f1-558667fdcf41\",\"add\":7161,\"del\":868}]"}
    563 views
   Owned this note