#include #include #include #include #define SOUBOR_VSTUP "mapy.in" #define SOUBOR_VYSTUP "mapy.out" // Formát vstupu: // Na prvním řádku číslo N udávající počet kusů mapy, na dalších N řádcích pak // vždy čtveřice čísel, souřadnice levého vrchního a pravého dolního rohu // každé mapy. // Na dalším řádku číslo Q a na dalších Q řádcích vždy dvě čísla udávající // souřadnice dotazu. // N a Q jsou celá čísla, souřadnice floaty. // // Formát výstupu: // Q řádků (pro každý dotaz jeden) s jedním číslem - počtem kusů map, ve // kterých leží zadaný bod. Počítáme i body na hranicích map // Datový typ pro zpracovávané události při zametání a vyrábění pásů typedef struct { float x, y1, y2; bool zacatek; } tudalost; // Jednotný typ používaný ve stromech pro obě osy typedef struct tvrchol { float leva_mez, prava_mez; struct tvrchol* levy; struct tvrchol* pravy; struct tvrchol* hranice; // Hranice těsně na začátku pruhu, kde se mohou setkávat dva okraje struct tvrchol* pruh; int pocet; int pas_id; } tvrchol; int N, Q; tvrchol* koren_x; tvrchol* koren_y; int pas_id; int udalost_index; tudalost* udalosti; // Pomocná funkce vyškrtávající z utříděného pole opakující se hodnoty, vrací // počet zbylých hodnot int unique(float arr[], int max) { int k = 0, i = 0; float last = NAN; while (i < max) { if (last != arr[i]) arr[k++] = arr[i++]; else i++; last = arr[i-1]; } return k; } // Dvě porovnávací funkce pro použití s knihovním qsort int floatcmp (const void * a, const void * b) { float fa = *(float*) a; float fb = *(float*) b; return (fa > fb) - (fa < fb); } int udalostcmp (const void * a, const void * b) { tudalost ua = *(tudalost*) a; tudalost ub = *(tudalost*) b; return 10*((ua.x > ub.x) - (ua.x < ub.x)) - ((ua.zacatek) - (ub.zacatek)); } // Pomocná funkce pro výpis intervalového stromu (pro testování) void print_interval_tree(tvrchol* vrchol, int offset) { if (vrchol == NULL) return; for (int i = 0; i < offset; i++) printf("| "); printf("[%f -> %f][id:%d]:\t%d\n", vrchol->leva_mez, vrchol->prava_mez, vrchol->pas_id, vrchol->pocet); print_interval_tree(vrchol->levy, offset+1); print_interval_tree(vrchol->pravy, offset+1); } // Vyrobí prázdný vyhledávací intervalový strom podle zadaných intervalů tvrchol* make_tree(float arr[], int left, int right) { tvrchol* vrchol = (tvrchol*)malloc(sizeof(tvrchol)); vrchol->pocet = 0; vrchol->pas_id = 0; if (left == right) { // List vrchol->levy = NULL; vrchol->pravy = NULL; if (left == 0) vrchol->leva_mez = -INFINITY; else vrchol->leva_mez = arr[left - 1]; vrchol->prava_mez = arr[right]; } else { int middle = left + (right - left) / 2; vrchol->levy = make_tree(arr, left, middle); vrchol->pravy = make_tree(arr, middle + 1, right); vrchol->leva_mez = vrchol->levy->leva_mez; vrchol->prava_mez = vrchol->pravy->prava_mez; } return vrchol; } // Zajistí aktualizaci intervalového stromu, přičtení hodnoty k danému intervalu. // Zároveň zajišťuje perzistenci, pokud by totiž měla modifikovat vrchol, který // je již součástí stromu v jiném pásu (má jiné pas_id), vyrobí namísto něj // vrchol nový tvrchol* update_interval(tvrchol* vrchol, int pas_id, float left, float right, int hodnota) { if (left < vrchol->leva_mez) left = vrchol->leva_mez; if (right > vrchol->prava_mez) right = vrchol->prava_mez; // Tento vrchol nebo jeho syny budeme určitě modifikovat, zajistíme si // tedy vytvoření kopie pro tento pás, pokud už na ní náhodou nejsme if (vrchol->pas_id != pas_id) { tvrchol* novy = (tvrchol*)malloc(sizeof(tvrchol)); novy->leva_mez = vrchol->leva_mez; novy->prava_mez = vrchol->prava_mez; novy->levy = vrchol->levy; novy->pravy = vrchol->pravy; novy->pocet = vrchol->pocet; novy->pas_id = pas_id; vrchol = novy; } if (vrchol->leva_mez == left && vrchol->prava_mez == right) vrchol->pocet += hodnota; else { if (left < vrchol->pravy->leva_mez) vrchol->levy = update_interval(vrchol->levy, pas_id, left, right, hodnota); if (right > vrchol->levy->prava_mez) vrchol->pravy = update_interval(vrchol->pravy, pas_id, left, right, hodnota); } return vrchol; } // Přiřadí správné vyhledávací Y-stromy druhé úrovně do listů X-stromu (vyrobí // jednotlivé pruhy). Y-stromy vyrábí vždy z předchozího modifikací pomocí // volání update_interval() void make_columns(tvrchol* vrchol) { if (vrchol->levy == NULL && vrchol->pravy == NULL) { // Projdeme všechny události od aktuálního indexu až do pravé // meze tohoto vrcholu, modifikujeme vnořený strom pro pás // a tento nový strom uložíme v tomto vrcholu. tvrchol* hranice = NULL; while (udalost_index < 2*N && udalosti[udalost_index].x < vrchol->prava_mez) { tudalost u = udalosti[udalost_index++]; // Jak začneme zpracovávat konce, již to nepočítáme do hranice if (!u.zacatek && hranice == NULL) { hranice = koren_y; pas_id++; } koren_y = update_interval(koren_y, pas_id, u.y1, u.y2, u.zacatek ? 1 : -1); } // Díky perzistenci je teď v koren_y nový kořen bez toho, abychom // ovlivnili kořeny v již zpracovaných pásech if (hranice == NULL) hranice = koren_y; vrchol->hranice = hranice; vrchol->pruh = koren_y; pas_id++; // Testování //print_interval_tree(hranice, 0); //print_interval_tree(koren_y, 0); //printf("-----------\n"); } else { make_columns(vrchol->levy); make_columns(vrchol->pravy); } } int main() { // 1. Načtení vstupu FILE *vstup = fopen(SOUBOR_VSTUP, "r"); fscanf(vstup, "%d", &N); // Struktura pro uchovávání kusů map udalosti = (tudalost*)malloc(sizeof(tudalost) * 2 * N); float* osa_x = (float*)malloc(sizeof(float) * 2 * N + 1); float* osa_y = (float*)malloc(sizeof(float) * 2 * N + 1); int x = 0, y = 0; for (int i = 0; i < 2*N; i+=2) { udalosti[i].zacatek = true; udalosti[i+1].zacatek = false; fscanf(vstup, "%f %f %f %f", &udalosti[i].x, &udalosti[i].y1, &udalosti[i+1].x, &udalosti[i+1].y2); udalosti[i].y2 = udalosti[i+1].y2; udalosti[i+1].y1 = udalosti[i].y1; osa_x[x++] = udalosti[i].x; osa_x[x++] = udalosti[i+1].x; osa_y[y++] = udalosti[i].y1; osa_y[y++] = udalosti[i+1].y2; } qsort(osa_x, 2*N, sizeof(float), floatcmp); qsort(osa_y, 2*N, sizeof(float), floatcmp); int x_uniq = unique(osa_x, 2*N); int y_uniq = unique(osa_y, 2*N); osa_x[x_uniq] = INFINITY; osa_y[y_uniq] = INFINITY; // 2. Vyrobíme si dva intervalové stromy - O(N): // X-strom na pásy (intervaly podle osy x), bude obsahovat odkazy na další stromy // Y-strom jako vzor pro stromy pásů (podle osy y), na začátku bude obsahovat všude 0 koren_x = make_tree(osa_x, 0, x_uniq); koren_y = make_tree(osa_y, 0, y_uniq); // 3. Postupně "zameteme" všechny události mapy uspořádané odleva a po // zpracování všech změn z aktuálního pásu je uložíme do správného listu // X-stromu: qsort(udalosti, 2*N, sizeof(tudalost), udalostcmp); // O(N log N) pas_id = 0; udalost_index = 0; make_columns(koren_x); // O(N log^2 N) // 4. Zpracování dotazů - O(Q log N) FILE *vystup = fopen(SOUBOR_VYSTUP, "w"); fscanf(vstup, "%d", &Q); for (int i = 0; i < Q; i++) { float Qx, Qy; fscanf(vstup, "%f %f", &Qx, &Qy); // Najdeme pruh - O(log N) tvrchol* pruh = koren_x; while (pruh->levy != NULL) { if (Qx < pruh->pravy->leva_mez) pruh = pruh->levy; else pruh = pruh->pravy; } tvrchol* bod = (Qx == pruh->leva_mez ? pruh->hranice : pruh->pruh); // Najdeme v pruhu správný bod - O(log N) int pocet = 0; while (bod->levy != NULL) { pocet += bod->pocet; if (Qy < bod->pravy->leva_mez) bod = bod->levy; else bod = bod->pravy; } pocet += bod->pocet; fprintf(vystup, "%d\n", pocet); } fclose(vstup); fclose(vystup); return 0; }