Opis
BOI 2016
Bosses
Park
Spiral
Cities
Maze
Swap
CEOI 2017
Skierowanie dróg
Pewny zakład
Pułapka Mariusza
Budujemy mosty
Pościg Mariusza
Palindromiczny podział
BOI 2018
Miłosny wielokąt
Marsjańskie DNA
Problemy dżdżownicy
Prąd przemienny
Genetyka
Ścieżki
BOI 2019
Flash Memory
Nautilus
Alpine Valley
Tom's Kitchen
Necklace
Olympiads
BOI 2020
Kolory
Mieszanki
Joker
Graf
Wioska
Wirusy
CEOI 2020
Fajny płot
Drogi
Star Trek
Napój Wielkiej Potęgi
Wiosenne porządki
Szachowa gorączka

Swap

Dana jest permutacja $n$ liczb. Wykonujemy na niej $n-1$ kroków, w $i$-tym kroku (dla $i=2,3,\ldots,n$) możemy zamienić miejscami elementy na pozycjach $i$ oraz $\lfloor i/2 \rfloor$. Jaką minimalnie leksykograficznie permutację możemy uzyskać?

Rozwiązanie

Kluczem do rozwiązania jest połączenie rozwiązania zachłannego oraz leniwego podejmowania decyzji. Permutacja $\pi_1$ jest leksykograficznie mniejsza niż permutacja $\pi_2$, jeśli pierwszy element $i$ na którym się różnią spełnia $\pi_1(i) < \pi_2(i)$. Zatem możemy przeglądać elementy od lewej do prawej i za każdym razem próbować ustawić aktualnie przeglądany element na najmniejszy możliwy.

O tym, czy zamieniamy elementy miejscami, będziemy decydować leniwie. Na początku dla żadnego kroku nie mamy podjętej decyzji. Następnie, podczas przeglądania elementu $i$, patrzymy jakie decyzje musielibyśmy podjąć, żeby $\pi(i)$ stało się najmniejsze możliwe. W kolejnych krokach będziemy musieli respektować podjęte decyzje.

Informacje o wartościach permutacji będziemy reprezentować w postaci acyklicznego grafu. Wierzchołek node grafu może być dwóch rodzajów: albo ustaloną wartością, albo wartością zależną od pewnej decyzji.

Jeśli jest ustaloną wartością, to trzymamy ją w składowej value. Jeśli natomiast zależy od pewnej decyzji, to trzymamy składowe: fixed -- wskaźnik na informację o decyzji, le -- wskaźnik na wierzchołek, który zawiera informacje o wartości w przypadku, gdy decyzja będzie „nie zamieniamy”, ri -- wskaźnik na wierzchołek, który zawiera informacje o wartości dla decyzji „zamieniamy”.

Dla wierzchołka potrzebujemy dwóch operacji: ustalenie jaka jest jego najmniejsza możliwa wartość oraz wykonanie niezbędnych decyzji, żeby ustawić taką wartość.

W przypadku wyznaczania minimum, przechodzimy po grafie. Dla wierzchołka z ustaloną wartością zwracamy value, a dla nieustalonego patrzymy na jego decyzję. Jeśli nie była podjęta, to zwracamy minimum z rekurencyjnie obliczonych wartości dla wierzchołków le i ri, a jeśli była, to wartość z odpowiedniego z tych wierzchołków.

Mając minimalną wartość, jeszcze raz przechodzimy graf, aby ustalić wymuszone decyzje. Dla wierzchołka bez podjętej decyzji ustalamy ją tak, aby uzyskać minimum. Dla uproszczenia implementacji, jeśli decyzja będzie „zamieniamy”, to możemy po prostu zamienić miejscami wartości wierzchołków le i ri.

Poniżej implementacja struktury wierzchołka:

struct node {
  node* le;
  node* ri;
  bool* fixed;
  int value;

  int minimum() {
    if (!le) {
      return value;
    } else if (*fixed) {
      return le->minimum();
    } else {
      return min(le->minimum(), ri->minimum());
    }
  }

  void fix(int val) {
    if (!le) {
      assert(val == value);
    } else {
      if (!*fixed && ri->minimum() == val) {
        swap(*le, *ri);
      }
      *fixed = true;
      le->fix(val);
    }
  }
};

Teraz wystarczy stworzyć $n$ wierzchołków zawierających ustalone wartości permutacji, oraz $2(n-1)$ wierzchołków zależących od decyzji. W tym celu zastępujemy wierzchołek $i$-ty nowym wierzchołkiem zależnym od decyzji $d$, który w przypadku podjęcia decyzji „nie zamieniamy” daje starą wartość wierzchołka $i$, a w przypadku dezyzji „zamieniamy” daje starą wartość wierzchołka $i/2$. Analogicznie zastępujemy wierzchołek $i/2$, przy czym decyzja $d$ jest ta sama, ale role starych wartości są zamienione.

Na końcu $n$ razy wyznaczamy minimum i ustalamy decyzje. Zauważmy, że w każdym kroku możemy pozwolić sobie na przejrzenie wszystkich wierzchołków osiągalnych z $i$-tego wierzchołka. Z konstrukcji drzewa wynika bowiem, że dla każdego wierzchołka-decyzji jedno z poddrzew jest jednowierzchołkowe (jest to wierzchołek-wartość), a drugie poddrzewo jest zaczepione w wierzchołku, którego indeks jest dwa razy mniejszy. Zatem liczba wierzchołków osiągalnych jest równa co najwyżej $O(\log n)$.

Tak więc całe rozwiązanie będzie miało złożoność czasową $O(n \log n)$:

const int N = 210000;
int n,a[N];
node* no[N];
bool is_fixed[N];

int main() {
  scanf("%d",&n);
  REP(i,n) {
    scanf("%d",&a[i]);
    no[i] = new node{ nullptr, nullptr, nullptr, a[i] };
  }
  REP(i,n-1) {
    node* le = no[i/2];
    node* ri = no[i+1];

    no[i/2] = new node{ le, ri, &is_fixed[i], -1 };
    no[i+1] = new node{ ri, le, &is_fixed[i], -1 };
  }

  REP(i,n) {
    int v = no[i]->minimum();
    no[i]->fix(v);
    
    printf("%d ",v);
  }
  printf("\n");
}