Быстрые операции над многочленами ч.1. (22 января 2024)

  1. Действия над F2 (bitset, разминка)
    1. Умножение за O(nm/w): с = a << i
    2. Деление за O((n-m)m/w): с[i] = 1; a = b << i
    3. gcd за O(nm/w) // (n+m)min(n,m) = Θ(nm)

  2. Умножение многочленов
    1. Над F2 = O(nm/w)
    2. Над произвольным кольцом есть O(nm) и Карацуба O(nlog2(3)) ~= n1.58
    3. Над комплексными числами (значит, и R, и Z) есть FFT за O(nlogn) (повторим/изучим позже)

  3. Упражнение: 3SUM − n чисел от -m до m, выбрать 3 с суммой 0

  4. Разделяй и властвуй
    1. Перевод из одной системы счисления в другую за logn умножений
    2. Деление за logn умножений : nlog2n (ссылка на будущее и nlogn)

  5. Деление за nlog2n: подробности
    1. Определение деления: хотим из A длины n и B длины n получить такой C длины n, что у A и BC совпадают n старших коэффициентов
    2. Основная лемма: старшие k С зависят только от старших k A и старших k B
    3. Деление за квадрат: cn-1 = an-1/bn-1, ci = (ai - cn-1bi - cn-2bi+1 - ...) / bn-1
    4. Div(A,B,k) : |A|=|B|=k
    5. Разделяй и властвуй: Div(A,B,n): C1 = Div(A,B,n/2), C2 = Div(A-BC1,B,n/2), return concat(C1, C2)
    6. Время работы T(n) = 2T(n/2) + Mul(n) = или O(nlog2n), или O(nlog2(3)) в зависимости от реализации Mul

  6. Разделяй и властвуй для вычисления значения многочленов в точках (evaluation) за logn делений : nlog3n
    1. P(x0) = P(x) mod (x - x0); Q(x) = P(x) mod (x - x1)(x - x2)...(x - xk) ⇒ Q(xi) = P(xi)
    2. eval(P, x1, ... xk) = eval(P mod (x - x1)...(x - xk/2), x1, ... xk/2), eval(P mod (x - xk/2+1)...(x - xk), xk/2+1, ... xk)
    3. Время работы: T(k) = 2T(k/2) + Div(k) = O(klog2k)

  7. Разделяй и властвуй для интерполяции
    1. Ньютон: (x1...xn-1, y1...yn-1) → P(x) → P(x) + Q(x)/Q(xn)*(yn - P(xn)), где Q(x) = (x-x1)(x-x2)...(x-xn-1)
    2. Разделяй и властвуй: (x1...xn/2, y1...yn/2) → P(x) → P(x) + Q(x)*S(x), (x-x1)(x-x2)...(x-xn/2), а
      значения S(x) в точках xn/2+1...xn вычисляются как S(xi) = (yi - P(xi)) / Q(xi)

  8. [skipped] Извлечение корня за logn делений

  9. n! mod m и факторизация целых чисел
    1. k = floor(sqrt(n)), рассмотрим P(x) = x(x+k)(x+2k)...(x+k(k-1))
    2. P(1)P(2)P(3)...P(k) = (k2)!, чтобы дополнить до n!, сделаем руками O(k) умножений, время работы: O(n1/2log2n)
    3. Посчитать P(x) мы можем через разделяй и властвуй за O(klog2k)
    4. Умножение нужно теперь делать не в C, а по модулю m. Умножим в Z (то же, что R, что C), в конце возьмём по модулю m.
    5. Факторизация. min d: gcd(d!, n) != 1 - минимальный делитель n. Можно бинпоиском, можно "двоичными подъёмами" за O(n1/4log2n)

  10. FFT (напоминание)
    1. Общая схема: FFT(A), FFT(B), умножить поточечно, FFT-1
    2. Комплексные корни из единицы: картинка, группа, умножения и степени
    3. Рекурсивное FFT: идея + реализация
    4. Тренировка понимания 1: считаем 2n − nlogn или nlog2n?
    5. Тренировка понимания 2: перевод между системами счисления − nlogn или nlog2n?

  11. Где почитать? Лекция от sankowski

Быстрые операции над многочленами ч.2. (29 января 2024)

  1. Разминка: число счастливых билетов из 2n цифр
    1. P(x) = (x0 + x1 + ... + xd)n, ответ = сумма квадратов коэффициентов P(x)
    2. N = dn ⇒ возведение в степень работает за O(mul(N)) = O(NlogN)

  2. Применения FFT
    1. Умножение многочленов от двух переменных xiyj (y = xn ⇒ nmlog(nm))
    2. Покраска в k цветов (∑I независимо xIy|I|) : предподсчитали независимые за 2n, возводим в степень k за k умножений: O(k2nn2)
    3. Рюкзак, subsetsum+количество: набрать ровно k предметов с суммой ровно S за O(nSlogSlogn) (разделяйка, внутри fft: T(n,S) = 2T(n/2,S) + nS*log)
    4. Рюкзак, subsetsum за O~(n1/2S)

  3. Соединяем умножения над F2 − bitset и Карацубу
    1. Рекурсия Карацубы, когда n ≤ w, можно умножить за n2/w = O(w), оптимизация в wlog(3)-1 раз
    2. Рекурсия Карацубы, при подъёме сложение и вычитания превратились в xor за O(n/w), оптимизация в w раз
    3. Но время Карацубы будет T(n/w)*w, где T(n) = 3T(n/2) + 1 = w(n/w)log2(3) = nlog2(3) / wlog2(3)-1

  4. Умножение многочленов не в C
    1. Над кольцом Z/mZ, m − простое вида x*2s+1, n < 2s.
    2. Над Z (лучше всего по КТО для разных Z/mZ)

  5. Быстрая нерекурсивная версия FFT
    1. Предподсчёт корней root[i] = (cos,sin)
    2. Прямой ход рекурсии − перестановка массива коэффициентов A: F[rev[i]] = A[i], rev[i] = rev[i/2]/2 + ((i%2) << (k-1))
    3. Обратный ход рекурсии − FOR (s=0..k-2) { n = 2s; FOR (t=0; t<N; t+=2n) FOR (j=0; j<n; j++) { tmp = ...; F[t+j+n] = ...; F[t+j] = ...; }}

  6. Деление многочленов за O(nlogn)
    1. reverse(A) = AR, заметим, что (BC)R = BRCR
    2. Определение деления новыми словами. A, B → C: AR = (BC)R mod xn
    3. Обращение ряда: получаем n первых членов обратного к A, за O(Mul(n)) AA-1 = 1 mod xn
    4. Получаем CR = (BR)-1AR (обращение ряда)

  7. Метод Ньютона и применение FFT к длинным числам
    1. Деление длинных чисел за O(nlogn)
    2. Извлечение корня за O(nlogn)