Processing math: 100%

środa, 6 marca 2013

13696. Drzwi Adriana [DALGO]

Zadanie:
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).

Podejście sprytne:
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