數論
喔不
質數
我最喜歡隨機了
判斷質數
如何判斷一個數是不是質數?
就找看看他有沒有 1 與他自己以外的因數
\(O(N)\)?
注意到(這是真的能注意到)\(\sqrt N\)以上都不會是 N 的因數
\(O(\sqrt N\))
一個判斷質數的隨機演算法
但是 N 在確定值域的情況下此演算法可以變成確定性演算法(不會猜錯)
複雜度為 \(O(k \times log ^ 3(N))\)
其中 k 為隨機取基數的次數
範例程式碼
#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize("O3", "unroll-loops")
#pragma GCC target("avx2")
using ll = long long;
ll modPow(ll a, ll d, ll n) {
ll ans = 1;
a %= n;
while (d > 0) {
if (d & 1) ans = (ans * a) % n;
a = (a * a) % n;
d >>= 1;
}
return ans;
}
bool millerRabin(int n) {
if (n < 2) return false;
if (n == 2 || n == 3) return true;
if (n % 2 == 0) return false;
// 將 n - 1 分解為 2^r * d,r, d為正整數,且 d 為奇數
int d = n - 1;
int r = 0;
while ((d & 1) == 0) {
d >>= 1;
r++;
}
int bases[] = {2, 7, 61};// 對於 n < 2 ^ 32,只需測試這三個 base 即可保證正確性(根據 deterministic Miller-Rabin)
for (int a : bases) {
if (a >= n) continue;
ll x = modPow(a, d, n); //x = a ^ (2 ^ s * d), s = 0
// 若存在某個 s 使得 a ^ (2 ^ s * d) ≡ -1 mod n,則 a 是 "strong liar",n 可能是質數
if (x == 1 || x == n - 1) continue;
bool composite = true;
for (int i = 0; i < r - 1; i++) {
x = (x * x) % n;
if (x == n - 1) {
composite = false;
break;
}
}
if (composite) return false;// 若所有 s 都無法使 a ^ (2 ^ s * d) ≡ -1 mod n,則 n 為合數
}
return true; //n is a Prime
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n;
while(cin >> n) {
if(millerRabin(n))cout << "質數\n";
else cout << "非質數\n";
}
return 0;
}數質數
給定一個 N,求 2 ~ N 範圍內的質數數量
我可以對每一個數字做質數判斷
\(O(N \times \sqrt N)\)
有點慢
埃氏篩
做一個 bool 陣列,裡面都放 true
false 代表合數,true 代表質數
從 2 開始跑,首先我發現 2 是質數,所以我跑到\(2^2 = 4\)的位置,把後面所有 2 的倍數都設成 false
跑到 3,發現他是質數,所以我跑到\(3^2 = 9\)的位置,把後面所有 3 的倍數都設成 false
跑到 4,發現他是合數,跳過
以此類推
直到跑到大於\(\sqrt N\)的位置
複雜度 \(O(N \times log (logN))\)
線性篩
發現埃式篩會重複地把一些已經是合數的數字再修成合數
而線性篩讓每個合數只被自身最小的質因數篩選過一遍
一樣做一個 bool 陣列,裡面都放 true(質數)
同時開一個 vector,裝目前已知的質數
從 2 開始,他是質數,所以把他丟到 vector 裡
接下來對於遍歷這個 vector,把裡面的質數乘以目前跑到的數
如果太大就離開遍歷,否則把該數改成合數
並且如果目前此數為質數的倍數,則也要離開遍歷
複雜度 \(O(N)\)
打表題
模運算
取餘數
符號
\(a \equiv 3 \mod p \)
即為 a 除以 p 的餘數等於 3 除以 p 的餘數
加減乘除
(a + b) % p == (a % p + b % p) % p
(a - b) % p == (a % p - b % p) % p
(a * b) % p == ((a % p) * (b % p)) % p
(a / b) % p != ((a % p) / (b % p)) % p
很容易找到反例
費馬小定理
首先如果 p 為質數,且 a 不是 p 的倍數
則 \(a^p \equiv a\mod p\)
左右同除 a 變成 \(a^{p - 1} \equiv 1\mod p\)
模逆元
對於 a 的模逆元 b,要滿足以下條件
\(a \times b \equiv 1 \mod p\)
而剛剛說了 \(a^{p - 1} \equiv 1\mod p\)
所以兩邊再同除 a 變成 \(a^{p - 2} \equiv a^{-1}\mod p\)
\(a^{-1} = b\)
所以\(a^{p - 2} \equiv b\mod p\)
這時,對於任意一個整數 x,\(x \div a \mod p = x \times b \mod p\)
而 \(a^{p-2} \mod p\) 可以用快速冪計算
費馬小定理
p-1?
#include <bits/stdc++.h>
using namespace std;
int main()
{
long long int n,a,b,c,p=1000000007,ans1,ans2;
cin>>n;
while(n--){
ans1=1;
ans2=1;
cin>>a>>b>>c;
p--; // why?
for(;c;c>>=1,b=b*b%p)if(c&1)ans1=(ans1*b)%p;
p++;
for(;ans1;ans1>>=1,a=a*a%p)if(ans1&1)ans2=(ans2*a)%p;
cout<<ans2<<'\n';
}
return 0;
}數學
首先\(a^{b^c}\)將改成 \(a^X\)
根據費馬小定理,\(a^{p-1} \equiv 1 \mod p\)
而我們可以將\(X\)改成\(q \times (p-1) + r\)的形式
所以\(a^{b^c} = a^{q \times (p-1) + r}\)
\(= a^{q \times (p-1)} \times a^r\)
\(a^{q \times (p-1)} \times a^r \equiv a^r \mod p\)
而\(X \equiv r \mod p - 1\)
所以 \(a^{b^c} \equiv a^{(b^c) \mod p-1} \mod p\)
模逆元
分塊
區間總和
給定一個陣列,求一個範圍內的數值總和
\(O(N)\)爆搜?太慢
\(O(log(N))\)線段樹?欸你太快了這是下學期的
切塊
我把陣列切成好幾個等大的分塊
每個分塊存取其區間的總和
如果查詢的區間範圍涵蓋至少一個分塊
就可以直接把該分塊的值加進答案
複雜度
\(O(N/k + k)\),其中k是分塊大小
然後你會算幾不等式
\((N/k + k)/2 \geq \sqrt N\)
所以當 k 為 \(\sqrt N\)時,單次查詢的複雜度最小,為\(O(\sqrt N\))
單點修改
修改值,然後改分塊存的值
對就這樣
區間修改
如果範圍涵蓋至少一個分塊的話
把被完全覆蓋的分塊更新
其餘的值就一個一個一個更新
題目
數論
By ck11300768鄭博軒
數論
- 70