Теория Чисел ~ Сириус 05.06
Основная теорема арифметики
Основная теорема арифметики гласит, что каждое число раскладывается на простые множители, причем единственным образом
Факторизация за O(sqrtN)
Пусть у нас есть число N
Оно представимо в виде N = a * b. Заметим, что одно из чисел a и b <= sqrt(n) (иначе a * b > n) => можно перебирать делители N до его корны
Решето Эратосфена за O(NLogLogN)
//БУДЬТЕ АККУРАТНЕЙ С ПЕРЕПОЛНЕНИЕМ!!! int n; vector<bool> isPrime(n); for(int i = 2; i <= n; i++){ isPrime[i] = true; } for(int i = 2; i * i <= n; i++){ if(isPrime[i]){ for(int j = i * i; j <= n; j++){ isPrime[j] = false; } } }
Прикол с minDiv (чтобы факторизацию делать)
int n; vector<int> minDiv(n); vector<bool> isPrime(n); for(int i = 2; i <= n; i++){ isPrime[i] = true; minDiv[i] = i; } for(int i = 2; i * i <= n; i++){ if(isPrime[i]){ for(int j = i * i; j <= n; j++){ isPrime[j] = false; minDiv[j] = min(minDiv[j], i); } } }
Диофантовы уравнения
Уравнения вида a*x + b*y = c (a, b, c - известны)
Прикол:
Если (a, b) = g, то существуют x и y такие, что a*x + b*y = g
(a, b) -> (b, a % b)
b * x1 + (a % b) * y1 = g
(Для тех, кто не знал - a % b = a - [a / b] * b)
b*x1 + (a - [a / b] * b) * y1 = a * y1 + b * (x1 - [a / b] * y1)
Пример шапки функции:
int gcd(int a, int b, int &x, int &y){ ... }
Заметим, что a * (x + b) + b (y - a) = a * x + b * y => x = x0 + t * b/g;
y = y0 - t * a/g
Теперь решим основную задачу
Воспользуемся тем, что мы умеем решать для НОДа и тупо домножим коэффициенты.
Если c % g == 0 => решений бесконечно много
Если c % g != 0 => решений нет
Функция Эйлера
ф(n) (да, это фи) - количество взаимнопростых чисел, которые меньше n
ф(10) = 4
Так же интересное свойство:
ф(a) * ф(b) = ф(a*b) для (a, b) = 1 (я не собираюсь это доказывать, так как а) это никому не нужно, б) это долго и скучно)
Тогда достаточно просто считается ф(n) для любого n
ф(n) = ф(p1^L1 * p2^L2 * p3^L3 * ... ) = ф(p1^L1) * ф(p2^L2) * ... = (тупое свойство 2) = n * (p1 - 1 / p1) * ... * (pK - 1 / pK)
(лучше считать через ans / p, ans *= (p - 1)
int n; int ans = n; for(int i = 2; i * i <= n; i++){ if(n % i == 0){ ans /= i; ans *= (i - 1); while(n % i == 0){ n /= i; } } } if(n > 1){ ans /= n; ans *= (n - 1); }
Количество делителей числа
Пусть у нас n = p1^L1 + p2^L2 + p3^L3 + ... + pK^LK
Тогда, кол-во делителей = (L1 + 1) * (L2 + 2) * (L3 + 3) * ... * (LK + 1) (очевидно)
Малая Теорема Ферма
Теорема Эйлера
Функции модульной арифметики
const int MOD = 998244353; int add(int a, int b){ if(a + b >= MOD){ return a + b - MOD; } else{ return a + b; } } int sub(int a, int b){ if(a - b < 0){ return a + b - MOD; } else{ return a - b; } }
Китайская теорема об остатках
Мем с модулями
a * g ≡ b * g (mod m * g) -> a ≡ b (mod m)
a * g ≡ b * g (mod m) -> a ≡ b (mod m) при (m, g) = 1
a ≡ b (mod m) -> a + x ≡ b + x (mod m)
С из N по K
(n, k) = n! / (k! * (n - k)!)
n! = (n - 1)! * n
надеюсь я с комментариями нигде не напутал...
int n; int f[n], rf[n]; for(int i = 1; i < n; i++){ f[i] = mul(f[i - 1], i); //умножение чисел по модулю } rf[n - 1] = inv(f[n - 1]); //обратное число по модулю for(int i = n - 2; i >= 0; i--){ rf[i] = mul(rf[i + 1], i + 1); //умножение чисел по модулю }
Задачка
Надо найти все простые числа на отрезке [L, R]
(R-L+1 < 1e7)
int l, r; vector<int> primes; //предпосчитать vector<bool> isPrime(r - l + 1, true); for(auto &i : primes){ for(int j = i * max((l + i - 1) / i, i); j <= r; j += i){ isPrime[j - l] = false; } }
Линейное решето Эратосфена
int n; vector<int> minDiv(n); vector<int> primes; iota(minDiv.begin(), minDiv.end(), 0); for(int i = 2; i < n; i++){ if(minDiv[i] == i){ primes.pb(i); } for(auto &p : primes){ if(p > minDiv[i] || p * i >= n){ break; } minDiv[p * i] = p; } }