Zoznam úloh

3. Vystupovanie

Zadanie

Každý víkend sa Jozef Hovnivál potreboval dostať domov z rodinného výmetu, aby si mohol sadnúť na svoju pohodlnú stolicu. Chodieval autotrusom. No aby to nemal také jednoduché, zakaždým sa rozhodol, akú dlhú prechádzku si chce spraviť domov z autotrusovej zastávky. Teda vystúpil na takej zastávke, ktorá je od jeho domu v správnej vzdialenosti. No toto ho rýchlo omrzelo, pretože chodil často tou istou trusou. Tak si vymyslel ešte jednu podmienku, a to že nepôjde z ktorejkoľvek zastávky, ktorá je v správnej vzdialenosti, ale z $k$-tej takej v poradí. Teda napríklad sa mohol rozhodnúť, že vystúpi na tretej zastávke, z tých, ktoré sú od jeho domu vzdialené 7 metrov. Jozefovi Hovniválovi ale zaberalo veľmi veľa času zistiť, na ktorej zastávke má vystúpiť. Preto potrebuje vašu pomoc.

Úloha

Na vstupe dostanete $n$ čísel v rozsahu od $1$ po $1\,000\,000$. Toto sú vzdialenosti jednotlivých zastávok od jeho domu, v takom poradí, v akom ich prejde autotrus. Potom dostanete $q$ otázok. Každá otázka pozostáva z čísla zastávky $l$, na ktorej Jozef nastúpi, čísla zastávky $r$, na ktorej mu skončí platnosť lístka (a teda nemôže pokračovať ďalej v ceste autotrusom). Ďalej dostanete dĺžku prechádzky $v$, ktorú si Jozef praje a nakoniec na koľkej takej zastávke chce Jozef vystúpiť, $k$. Vašou úlohou je vypísať číslo zastávky, ktorá sa nachádza v intervale od $l$ po $r$ vrátane, jej vzdialenosť od domu je $v$ a je to $k$-ta taká zastávka v danom intervale. Ak taká neexistuje, vypíšte $-1$. Číslujeme od jednotky.

Formát vstupu

Na prvom riadku vstupu sa nachádzajú čísla $n$ a $q$ oddelené medzerou, počet zastávok a počet otázok. Na druhom riadku sa nachádza $n$ čísel oddelených medzerami, vzdialenosti zastávok od Jozefovho domu. Nasleduje $q$ riadkov, na každom z ktorých sa nachádzajú medzerami oddelené čísla $l$, $r$, $v$ a $k$, ktorých význam je vysvetlený vyššie.

Formát výstupu

Vypíšte $q$ riadkov výstupu. Na $i$-tom riadku sa nachádza odpoveď na $i$-tu otázku, teda číslo zastávky, ktorá spĺňa požadované vlastnosti, alebo $-1$ ak taká neexistuje.

Hodnotenie

Je 8 sád vstupov. Platia v nich nasledujúce obmedzenia:

Sada 1–2 3 4 5–8
$1 \leq n \leq$ $100$ $10\,000$ $80\,000$ $100\,000$
$1 \leq q \leq$ $100$ $10\,000$ $100\,000$ $100\,000$

V prvej sade navyše platí, že vzdialenosti zastávok od domu sú z množiny ${1, 2}$.

Príklad

Input:

5 4
3 4 5 4 5
2 3 4 1
3 4 1 2
1 2 4 1
1 2 4 2

Output:

2
-1
2
-1

Input:

10 5
3 3 1 3 3 2 1 3 3 1
1 5 3 1
1 8 3 1
2 8 3 2
1 4 1 1
1 6 1 2

Output:

1
1
4
3
-1

Pomalé riešenie

Ako prvé si predstavíme priamočiare riešenie bez žiadnych trikov. Budeme jednoducho vstupné pole lineárne prehľadávať počnúc od indexu $l$ a počítať, koľkokrát sme už narazili na zastávku v správnej vzdialenosti $v$ od Jozefovho domu. Akonáhle narazíme na $k$-tu takú, vypíšeme jej index. Ak ale prekonáme index $r$ pred tým, ako nájdeme odpoveď, odpovieme $-1$. Toto riešenie by bolo optimálne, v prípade, keby sme dostali len $q = 1$ otázok. To, že otázok môže byť veľa, nám ale napovedá, že sa asi dá najprv so vstupnými údajmi vykonať nejaká transformácia, ktorá nám potom umožní odpovede zisťovať rýchlejšie ako v lineárnom čase.

Vzorové riešenie

Kľúčový trik k riešeniu úlohy je nasledujúci: Pre každú vzialenosť $v$ od domu si vytvoríme pole, v ktorom sa nachádzajú čísla (indexy) zastávok, ktoré majú túto vzdialenosť. Napríklad ak je na vstupe pole: $[3, 4, 5, 4, 5]$ tak by sme si vytvorili v pamäti štruktúru podobnú nasledujúcej:

`X[3]: 1
X[4]: 2 4
X[5]: 3 5`

Na jej uskladnenie môžeme použiť buď hash mapu (dict) alebo jednoduché pole veľkosti $10^6$, pretože to je maximálna hodnota $v$.

Všimnime si, že každé pole $X[v]$ je usporiadané. Teraz vieme na otázky odpovedať nasledujúcim postupom: Pozrieme sa do poľa $X[v]$ a nájdeme v ňom najmenšiu hodnotu väčšiu alebo rovnú $l$. Ako na to? Postupné prechádzanie po jednom funguje, ale má zložitosť $O(n)$, takže si moc nepomôžeme. Zachráni nás ale binárne vyhľadávanie, ktoré to spraví v $O(\log n)$. Zastávka, ktorú takto nájdeme, je prvá taká, cez ktorú prejde autobus s Jozefom a je v správnej vzdialenosti od jeho domu. My chceme ale $k$-tu, nie prvú, takže sa v tomto poli $X[v]$ pozrieme o $k-1$ pozícii ďalej a nájdeme presne to, čo hľadáme. Ešte musíme ale vyriešiť dve prekážky: Ak by sme pri tomto procese vybehli z poľa, tak vieme, že taká zastávka neexistuje a odpovieme $-1$. Druhý problém je $r$ z otázky, no jednoducho sa stačí pozrieť na číslo zastávky, ktorú sme našli a ak je väčšie ako $r$, tak opäť vypíšeme $-1$, pretože zastávka už je mimo Jozefovej platnosti lístka.

Celková časová zložitosť riešenia (s použitím hash mapy) je $O(n+q\log n)$. $n$ pochádza z predpočítania štruktúry $X$ a $q\log n$ sú jednotlivé odpovede na otázky. Pamäťová zložitosť je $O(n)$, pretože jediné čo si pamätáme je $X$, pričom dokopy sa v ňom nachádza presne $n$ prvkov. Keby sme pre $X$ nepoužili hash mapu ale pole, zložitosti by boli trochu iné. Museli by sme na začiatku inicializovať pole veľkosti $10^6$, čo je určite viac ako $n$, pretože $n$ dosahuje len $10^5$. Taktiež si ho musíme pamätať. Ak by sme pomenovali $w = 10^6$ maximálnu hodnotu $v$, tak by časová zložitosť bola $O(w+n+q\log n)$ a pamäťová $O(w+n)$.

#include <vector>
#include <algorithm>
#include <iostream>

using namespace std;

int main() {
    int n, q;
    cin >> n >> q;
    vector<vector<int>> V(1000001);
    for (int i = 0; i < n; i++) {
        int x;
        cin >> x;
        V[x].push_back(i+1);
    }
    for (int i = 0; i < q; i++) {
        int l, r, v, k;
        cin >> l >> r >> v >> k;
        k--;
        int f = upper_bound(V[v].begin(), V[v].end(), l-1) - V[v].begin();
        if (f + k >= V[v].size() || V[v][f+k] > r)
            cout << -1 << '\n';
        else
            cout << V[v][f+k] << '\n';
    }
}
import bisect
from collections import defaultdict

def readints():
    return map(int, input().split())

n, q = readints()
X = list(readints())
# dict, ale ked pristupime k neexistujucemu prvku,
# tak automaticky vyrobi prazdny list
V = defaultdict(list)
for i, x in enumerate(X, 1):
    V[x].append(i)

for _ in range(q):
    l, r, v, k = readints()
    k-=1
    # Binarne vyhladavanie
    f = bisect.bisect(V[v], l-1)
    if f+k >= len(V[v]) or V[v][f+k] > r:
        print(-1)
    else:
        print(V[v][f+k])
Pre odovzdávanie sa musíš prihlásiť.
Trojsten

Korešpondenčný seminár z programovania zastrešuje občianske združenie Trojsten.

Kontakt
Ďalšie projekty