https://pl.spoj.com/problems/DALGO
Skrócony opis problemu:
Jest $n$ testów. Każdy z nich składa się z $3$ liczb: $p$, $q$ i $m$. Mając liczby $p$ i
$q$ należy znaleźć takie liczby $x$ i $y$, że $x \cdot p + y \cdot
q=NWD(p,q)$, ale tak, aby $x \ge 0$ oraz by $x$ było minimalne.
Następnie
należy nadpełnić przedział $\left<\min(x,y);\max(x,y)\right>$
liczbą $m$ (czyli do każdego elementu z przedziału dodać liczbę $m$). Po
wykonaniu wszystkich testów należy wypisać cały przedział.
Podejście naiwne:
W
równaniu $x \cdot p + y \cdot q=NWD(p,q)$ mamy dwie niewiadome. Możemy
zatem zacząć poszukiwanie $x$ od $0$ i mając już tylko jedną niewiadomą
sprawdzać czy istnieje takie całkowite $y$, że $y = \frac{NWD(p,q) - x
\cdot p}{q}$. Złożoność tej części to $O(n \cdot x)$ (bo jest n testów),
a jako że $x \approx n$, to możemy przyjąć $O(n^2)$.
Następnie
dla każdego testu wypełniamy liniowo przedział, a na samym końcu
wypisujemy wszystkie elementy przedziału. Złożoność tej części to:
$O\left(n \cdot \left(\left|x\right|+\left|y\right|\right) + n\right)$. Jako że
zarówno $x$ jak i $y$ są rzędu $n$, to otrzymujemy $O\left(n^2\right)$.
Widzimy, że złożoności obu części są takie same, więc ostateczna złożoność to również $O\left(n^2\right)$.
Zamiast obliczać $x$ i $y$ liniowo możemy skorzystać z rozszerzonego algorytmu Euklidesa.
Dostaniemy parę liczb $\left(X;Y\right)$ spełniającą powyższe równanie. Może się jednak zdarzyć, że $X<0$, jednak możemy to naprawić w czasie stałym. Dlaczego? Ustawmy sobie rozwiązania (pary liczb) w kolejności rosnącej względem składowej $x$. Na pewno otrzymamy parę, która byłaby sąsiadem pary $(0;0)$, gdyby taka istniała (czyli nie istnieje para o $x$ bliższym od $0$ niż nasze $X$ lub $y$ bliższym od $0$ niż nasze $Y$). Musimy więc tylko zamienić naszą parę na następną (czyli szukamy pary $(X2;Y2)$ takiej, że $X2>X$ oraz $X2$ jest minimalne). Mamy zatem takie równanie: $X \cdot p + Y \cdot q=NWD(p,q)$ i chcemy z jego pomocą rozwiązać takie: $(X+s) \cdot p + (Y-t) \cdot q=NWD(p,q)$, gdzie $s$ i $t$ to różnice między $X2$ a $X$ oraz $Y$ a $Y2$. $$X \cdot p + Y \cdot q=(X+s) \cdot p + (Y-t) \cdot q \\ X \cdot p + Y \cdot q=X \cdot p + s \cdot p + Y \cdot q - t \cdot q \\ s \cdot p = t \cdot q$$
Skoro chcemy znaleźć najmniejsze $s$, to musimy znaleźć najmniejsze dodatnie $t$, które po przemnożeniu przez $q$ dzieli się bez reszty przez $p$ (wynika to z: $s=\frac{t \cdot q}{p}$). Możemy przyjąć, że $t=p$, wtedy otrzymamy $s=q$. Czy $s$ jest jednak w tym przypadku najmniejsze możliwe? Nie. Weźmy np. $q=4$ i $p=2$. Wyszłoby nam, że $s=4$ a $t=2$. Najmniejsze $s$ to jednak $2$ przy $t=1$. W $q$ są bowiem czynniki pierwsze, które są również w $p$, i które się skrócą. Wzory na $s$ i $t$ to zatem:
$$s=\frac{q}{NWD(p,q)} \\ t=\frac{p}{NWD(p,q)}$$
Dlaczego napisałem, że możemy przeskoczyć z $X$ na $X2$ w czasie stałym, skoro obliczenie $NWD(p,q)$ wymaga logarytmicznego czasu? Dlatego, że $NWD(p,q)$ obliczyliśmy już dodatkowo w rozszerzonym algorytmie Euklidesa.
Złożoność tej części to $O\left(n \log{n}\right)$ (n testów - dla każdego rozszerzony algorytm Euklidesa).
Mamy już $x$ i $y$. Załóżmy, że już je uporządkowaliśmy i $x<y$. Jak szybciej niż ostatnio dodać $m$ do każdej komórki przedziału $\left<x;y\right>$? Otóż możemy skorzystać z drzewa przedziałowego. Uzupełniamy w czasie logarytmicznym ów przedział $\left<min(x[]);y\right>$ (gdzie $x[]$ oznacza tablicę wszystkich $n$ początków przedziałów, które będziemy uzupełniać) o wartość $m$, a następnie również logarytmicznie uzupełniamy przedział $\left<\min(x[]);x-1\right)$ o $-m$. Pierwsze uzupełnienie dopisze nam do wszystkich komórek od początku do $y$ liczbę $m$, a drugie usunie ją z przedziału od początku do $x-1$, jako że nie chcieliśmy modyfikować tamtych komórek. Odczytujemy dla każdej komórki analogicznie, w takim samym czasie asymptotycznym. Komórek takich może być maksymalnie $2n$.
Złożoność tej części to również $O\left(n \log{n}\right)$, jako że mamy $2n$ insertów oraz $2n$ query działających w czasie logarytmicznym.
Sumaryczna złożoność to zatem również $O\left(n \log{n}\right)$.
Podejście sprytniejsze:
W tym podejściu pierwszy etap się niczym nie różni od tego w poprzednim algorytmie. Skupimy się tylko na drugiej części.
Mamy już zatem $x$ i $y$ ($x<y$). Możemy zauważyć, że odczytujemy wszystkie komórki na raz. Gdybyśmy bowiem odczytywali tylko pojedyncze komórki to musielibyśmy robić to logarytmicznie, a tak możemy zrobić to w czasie stałym (więc łącznie liniowo). Do tablicy też dodajemy w czasie stałym. Robimy po prostu $tab[x] += m; tab[y+1] -=m;$ Wygląda to podobnie do poprzedniego rozwiązania, gdyż tam też dodawaliśmy gdzieś $m$, a gdzieś je odejmowaliśmy. Co nam da uzupełnienie tych dwóch komórek? Otóż przy wypisywaniu skorzystamy z sum prefiksowych. Zaczynamy od komórki $\min(x[])$, która ma jakąś wartość. Następnie idziemy do następnej i dodajemy do niej poprzednią wartość, a następnie ją wypisujemy. To $-m$ niweluje zatem poprzednie $m$, które dodaliśmy. Złożoność tej części to $O(n)$, jako że komórek może być do $2n$.
Asymptotyczna złożoność jest taka sama jak poprzednio, czyli $O\left(n \log{n}\right)$, jednak program działa trochę szybciej, a gdyby $x$ i $y$ były podane od razu, to złożoność by była liniowa.
Ostateczny algorytm:
int n; // n bez wartości oznacza wczytane n while(n--) { int p, q, m; int a = 1, d = 1, b = 0, c = 0, p2 = p, q2 = q, quot, r, new_d, new_c; while(q) // rozszerzony algorytm Euklidesa r = p%q, quot = p/q, p = q, q = r, new_c = a-quot*c, new_d = b-quot*d, a = c, b = d, c = new_c, d = new_d; if(a < 0) // jeśli a nie spełnia warunków a += q2/p, b -= p2/p; if(a < b) c = a, a = b, b = c; // zamiana a i b jeśli a>b if(a > max) max = a; // koniec ostatecznego przedziału if(b < min) min = b; // początek ostatecznego przedziału X[b+1000001] += m; // +1000001, aby na pewno indeks był nieujemny X[a+1000002] -= m; // +1000001+1 } for(int i = min; i <= max; ++i) printf("%d ", X[i+1000001] += X[i+1000000]); // najpierw dodajemy a potem wypisujemy komórkę
Brak komentarzy:
Prześlij komentarz