Submission #1281227

#TimeUsernameProblemLanguageResultExecution timeMemory
1281227SamueleVidMars (APIO22_mars)C++20
0 / 100
0 ms3196 KiB
#include <bits/stdc++.h>
using namespace std;
#define ll long long

struct dsu {
    vector<int> p;
    vector<int> size;

    dsu(int N) {
        p.resize(N);
        iota(p.begin(), p.end(), 1);
        size.assign(N, 1);
    }

    int boss(int u) {
        if (p[u] == u) return u;
        return p[u] = boss(p[u]);
    }

    void merge(int a, int b) {
        a = boss(a);
        b = boss(b);
        if (size[a] < size[b]) swap(a, b);
        size[a] += size[b];
        p[b] = a; 
    }
};

int get_islands(string s) {
    // negli ultimi 9 bit mi salvo il numero di componenti. 
    // Sto considerando le componenti sotto (o sopra) la diagonale, che ha come upperbound
    // (2N + 1)(2N + 1) / 4 = 420, nel caso della scacchiera. Questo numero sta in 9 bit perché è minore di 2^9 = 512.

    int islands = 0;
    for (int i = 0; i < 9; i ++) {
        if (s[100 - 1 - i] == '1') islands += (1 << i);
    }

    return islands;
}

vector<int> decode_components(string s) {
    // In 91 bit mi salvo le componenti dei quadratini sulla diagonale. 
    // Le componenti formano una sorta di stringa di parentesi:
    // questo perché tutti i nodi stanno su una linea, le connessioni si trovano su un semipiano e il grafo è planare.
    // Quindi ogni quadratino può essere scritto come uno tra questi simboli: 
    // o : 0 il quadratino è acqua
    // (. : 1 
    // . : 2
    // .) : 3
    // (.) : 4
    // Essendo 5 simboli, per comprimerli bene, invece di tenermi 3 bits per ognuno, prendo
    // 3 simboli consecutivi, li converto in base 5 e poi li converto in binario. 
    // Questo mi fa usare 7 bit per 3 quadratini invece di 9. 
    // Dato che la lunghezza più lunga della diagonale che mi interessa è (2N + 1 - 2) = 39.
    // Quindi mi bastano 39 * 7 / 3 = 91 bits per salvarmi le componenti sulla diagonale.
    
    vector<int> parenthesis;

    for (int b = 0; b < 91; b += 7) {
        // converto 7 bits in un numero
        int num = 0;
        for (int i = 0; i < 7; i ++) {
            if (s[b + i] == '1') num += (1 << i);
        }

        // converto questo numero in base 5 (di 3 cifre)
        vector<int> num_base_5;
        for (int c = 0; c < 3; c ++) {
            num_base_5.push_back(num % 5);
            num /= 5;
        }

        // ogni numero in base 5 corrisponde a uno dei 5 simboli della stringa di parentesi
        for (auto x : num_base_5) parenthesis.push_back(x);
    }

    vector<int> comps;

    int next_comp = 2; // inizio con 2 così lascio da parte gli 0 (vuoti) e gli 1 (ancora senza comps)
    stack<int> curr_comp;
    for (auto x : parenthesis) {
        if (x == 0) { // o
            comps.push_back(0);
        }
        if (x == 1) { // (.
            curr_comp.push(next_comp);
            next_comp ++;
            comps.push_back(curr_comp.top());
        }
        if (x == 2) { // .
            comps.push_back(curr_comp.top());
        }
        if (x == 3) { // .)
            comps.push_back(curr_comp.top());
            curr_comp.pop();
        }
        if (x == 4) { // (.)
            curr_comp.push(next_comp);
            next_comp ++;
            comps.push_back(curr_comp.top());
            curr_comp.pop();
        }
    }

    return comps;
}

string encode_components(vector<int> comps, int islands) {

    // mi salvo l'inizio e la fine di ogni gruppo per capire come vanno le parentesi
    map<int, int> inizio;
    map<int, int> fine;
    for (int i = 0; i < comps.size(); i ++) {
        if (comps[i] == 0) continue;
        if (!inizio.count(comps[i])) inizio[comps[i]] = i;
        fine[comps[i]] = max(fine[comps[i]], i);
    }

    // scrivo le parentesi
    vector<int> parenthesis(39, 0);
    for (int i = 0; i < comps.size(); i ++) {
        if (comps[i] == 0) { // o
            parenthesis[i] = 0;
        }
        if (inizio[comps[i]] == i && fine[comps[i]] == i) { // (.
            parenthesis[i] = 1; 
        }
        if (inizio[comps[i]] != i && fine[comps[i]] != i) { // .
            parenthesis[i] = 2;
        }
        if (inizio[comps[i]] != i && fine[comps[i]] == i) { // .)
            parenthesis[i] = 3;
        }
        if (inizio[comps[i]] == i && fine[comps[i]] == i) { // (.)
            parenthesis[i] = 4;
        }
    }

    string s;
    // alla fine del ciclo s sarà lunga 91 caratteri
    for (int p = 0; p < 39; p += 3) {
        // trasformo una sequenza contigua di tre simboli in un numero in base 5
        
        int num = 0;
        for (int c = 0; c < 3; c ++) {
            num *= 5;
            num += parenthesis[p + c];       
        }

        // ricopio il numero in binario su una substring lunga 7 in s
        for (int i = 0; i < 7; i ++) {
            if ((1 << i) & num) s.push_back('1');
            else s.push_back('0');
        }
    }

    // codifico il numero di isole negli ultimi 9 bit di s.
    for (int i = 0; i < 9; i ++) s.push_back('0');

    for (int i = 0; i < 9; i ++) {
        if ((1 << i) & islands) s[100 - 1 - i] = '1';
    }

    return s;
}

void calc_comps(vector<vector<int>> &grid, int islands, int diag, int N) {
    
    // esplicita le varie componenti dei quadratini non sulla diagonale
    int comps_num = 2;
    for (int i = 0; i < 2 * N + 1 && i - diag < 2 * N + 1; i ++) {
        int j = i - diag;
        comps_num = max(comps_num, grid[i][j]);
    }

    comps_num ++;
    for (int i = 0; i < 2 * N; i ++) {
        for (int j = 0; j < 2 * N; j ++) {
            // ignora gli elementi sulla diagonale
            if (i - j == diag) continue;

            if (grid[i][j]) {
                grid[i][j] = comps_num;
                comps_num ++;
            }
        }
    }

    dsu comps(comps_num + 3); // + epsilon per essere sicuro

    for (int i = 0; i < 2 * N - 1; i ++) {
        for (int j = 0; j < 2 * N; j ++) {
            if (grid[i][j] && grid[i + 1][j]) {
                comps.merge(grid[i][j], grid[i + 1][j]);

            }
        }
    } 
    for (int i = 0; i < 2 * N; i ++) {
        for (int j = 0; j < 2 * N - 1; j ++) {
            if (grid[i][j] && grid[i][j + 1]) {
                comps.merge(grid[i][j], grid[i][j + 1]);
            }
        }
    } 

    for (int i = 0; i < 2 * N; i ++) {
        for (int j = 0; j < 2 * N; j ++) {
            grid[i][j] = comps.boss(grid[i][j]);
        }
    }
}

string process(vector<vector<string>> a, int i, int j, int k, int N) {
    int m = 2 * (N - k - 1);
    
    if (i != m && j != m) {
        // non è sul bordo quindi lo lascio uguale
        return a[0][0];
    }

    if (k == N - 1) {

        vector<vector<int>> grid(2 * N + 1, vector<int>(2 * N + 1));
        for (int y = 0; y < 2; y ++) {
            for (int x = 0; x < 2; x ++) {
                grid[y][x] = (a[y][x][0] == '1') ? 1 : 0;
            }
        }
        for (int b = 0; 2 + b < 2 * N + 1; b ++) {
            grid[b + 2][b + 2] = (a[2][2][b] == '1') ? 1 : 0;
        }

        int islands = get_islands(a[2][0]) + get_islands(a[0][2]);

        vector<int> diag_sotto = decode_components(a[2][0]);
        // la condizione (b < diag_sotto.size()) è inutile perché ha sempre 2 * N - 1 elementi, e quelli in eccesso sono segnati come 0 
        for (int b = 0; b < diag_sotto.size() && 2 + b < 2 * N + 1; b ++) {
            grid[2 + b][b] = diag_sotto[b];
        }

        vector<int> diag_sopra = decode_components(a[0][2]);
        // la condizione (b < diag_sotto.size()) è inutile perché ha sempre 2 * N - 1 elementi, e quelli in eccesso sono segnati come 0 
        for (int b = 0; b < diag_sopra.size() && 2 + b < 2 * N + 1; b ++) {
            grid[b][2 + b] = diag_sopra[b];
        }

        calc_comps(grid, islands, i - j, N);

        set<int> visited_comps;

        for (int i = 0; i < 2 * N; i ++) {
            for (int j = 0; j < 2 * N; j ++) {
                if (grid[i][j] == 0) continue;
                if (visited_comps.count(grid[i][j])) continue;
                islands ++;
                visited_comps.insert(grid[i][j]);
            }
        }

        // scrivo il numero di isole in binario nel risultato
        string s;
        for (int i = 0; i < 100; i ++) s.push_back('0');

        for (int p = 0; p < 20; p ++) {
            if ((1 << p) & islands) s[p] = '1';
        }

        return s;
    }

    if (j == 0) {
        // mi salvo le componenti nel triangolo in basso a sinistra

        vector<vector<int>> grid(2 * N + 1, vector<int>(2 * N + 1));
        
        // y < 2 ricopio i singoli punti
        for (int y = 0; y < 2; y ++) {
            for (int x = 0; x < 3; x ++) {
                grid[i + y][j + x] = (a[y][x][0] == '1') ? 1 : 0;
            }
        }

        // y = 2, x > 1 devo ricopiare le diagonali sotto
        for (int x = 1; x < 3; x ++) {
            for (int b = 0; i + 2 + b < 2 * N + 1; b ++) {
                grid[i + 2 + b][x + b] = (a[2][x][b] == '1') ? 1 : 0;
            }
        }

        int islands = get_islands(a[2][0]);

        if (k != 0) {
            vector<int> diag_sotto = decode_components(a[2][0]);
            // la condizione (b < diag_sotto.size()) è inutile perché ha sempre 2 * N - 1 elementi, e quelli in eccesso sono segnati come 0 
            for (int b = 0; b < diag_sotto.size() && i + 2 + b < 2 * N + 1; b ++) {
                grid[i + 2 + b][b] = diag_sotto[b];
            }
        }
        else {
            // prima iterazione : inserisco il quadratino così com'è
            grid[i + 2][j] = a[2][0][0];
        }

        // calcola le componenti connesse.
        calc_comps(grid, islands, i - j, N);

        // e aggiorna le componenti
        vector<int> new_diag_sotto(2 * N + 1);
        for (int b = 0; i + 2 + b < 2 * N + 1; b ++) new_diag_sotto[i] = grid[i + 2 + b][b];

        set<int> visited_comps;
        for (auto x : new_diag_sotto) {
            if (x != 0) visited_comps.insert(x);
        }

        for (int i = 0; i < 2 * N; i ++) {
            for (int j = 0; j < 2 * N; j ++) {
                if (grid[i][j] == 0) continue;
                if (visited_comps.count(grid[i][j])) continue;
                islands ++;
                visited_comps.insert(grid[i][j]);
            }
        }

        string res = encode_components(new_diag_sotto, islands);

        return res;
    }

    
    if (i == 0) {
        // mi salvo le componenti nel triangolo in alto a destra.
        // il codice è analogo a quello sopra

        vector<vector<int>> grid(2 * N + 1, vector<int>(2 * N + 1));
        
        // x < 2 ricopio i singoli punti
        for (int x = 0; x < 2; x ++) {
            for (int y = 0; y < 3; y ++) {
                grid[i + y][j + x] = (a[y][x][0] == '1') ? 1 : 0;
            }
        }

        // x = 2, y > 1 devo ricopiare le diagonali sotto
        for (int y = 1; y < 3; y ++) {
            for (int b = 0; j + 2 + b < 2 * N + 1; b ++) {
                grid[y + b][j + 2 + b] = (a[y][2][b] == '1') ? 1 : 0;
            }
        }

        int islands = get_islands(a[0][2]);

        if (k != 0) {
            vector<int> diag_sopra = decode_components(a[0][2]);
            // la condizione (b < diag_sotto.size()) è inutile perché ha sempre 2 * N - 1 elementi, e quelli in eccesso sono segnati come 0 
            for (int b = 0; b < diag_sopra.size() && j + 2 + b < 2 * N + 1; b ++) {
                grid[b][j + 2 + b] = diag_sopra[b];
            }
        }
        else {
            // prima iterazione : inserisco il quadratino così com'è
            grid[i][j + 2] = a[0][2][0];
        }

        // calcola le componenti connesse.
        calc_comps(grid, islands, i - j, N);

        // e aggiorna le componenti
        vector<int> new_diag_sopra(2 * N + 1);
        for (int b = 0; i + 2 + b < 2 * N + 1; b ++) new_diag_sopra[i] = grid[i + 2 + b][b];

        set<int> visited_comps;
        for (auto x : new_diag_sopra) {
            if (x != 0) visited_comps.insert(x);
        }

        for (int i = 0; i < 2 * N; i ++) {
            for (int j = 0; j < 2 * N; j ++) {
                if (grid[i][j] == 0) continue;
                if (visited_comps.count(grid[i][j])) continue;
                islands ++;
                visited_comps.insert(grid[i][j]);
            }
        }

        string res = encode_components(new_diag_sopra, islands);

        return res;
    }

    // se è sui bordi però non è il triangolo (ne o so) allora semplicemente mi salvo la diagonale

    // parte dal primo carattere e poi scende piano piano in basso a destra
    string diagonal = a[0][0]; // comincia copiando il primo punto (e anche il resto vuoto, così è lungo 100)
    diagonal[1] = a[1][1][0]; // copio il secondo punto
    for (int b = 0; b + 2 < 100; b ++) {
        diagonal[b + 2] = a[2][2][b]; // ricopio la diagonale salvata in a[2][2], che era il bordo precedente.
    }

    // // max è 2n, max m è 2n - 2

    // the diagonal is (2n + 1 - m) bits long

    return diagonal;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...