Submission #1175886

#TimeUsernameProblemLanguageResultExecution timeMemory
1175886redstonegamer22Secret (JOI14_secret)C++20
100 / 100
349 ms5484 KiB
#include "secret.h"

#include <vector>
#include <iostream>
#include <map>
#include <utility>
#include <algorithm>
#include <cassert>

std::map<std::pair<int, int>, int> secret_map;
int mySecret(int X, int Y) {
    if(X == -2) return Y;
    if(Y == -2) return X;

    if(X == -1 || Y == -1) {
        return -1;
    }

    if(secret_map.find({X, Y}) != secret_map.end()) {
        return secret_map[{X, Y}];
    }

    return secret_map[{X, Y}] = Secret(X, Y);
}

struct DisjointSparseTable {
    int lg2;
    std::vector<std::vector<int>> table;

    DisjointSparseTable() {}

    DisjointSparseTable(std::vector<int> a) {
        {
            const int n = a.size();
            lg2 = 0;
            while ((1 << lg2) < n) {
                ++lg2;
            }
            assert((1 << lg2) >= n);
        }

        const int n = (1 << lg2);
        a.resize(n, -1);

        table.resize(lg2 + 1, a);

        for(int level = 1; level <= lg2; level++) {
            const int segment_size = (1 << level);

            for(int segment_start = 0; segment_start < n; segment_start += segment_size) {
                const int segment_end = segment_start + segment_size - 1;
                const int mid = segment_start + segment_size / 2 - 1;
                // segmentul meu e impartit la jumate ca [segment_start, mid] si [mid + 1, segment_end]
                
                for(int i = mid - 1; i >= segment_start; i--) {
                    table[level][i] = mySecret(table[level][i], table[level][i + 1]);
                }
                for(int i = mid + 2; i <= segment_end; i++) {
                    table[level][i] = mySecret(table[level][i - 1], table[level][i]);
                }
            }
        }
    }

    int query(int st, int dr) {
        if(st == dr) {
            return table[0][st]; // evident doar un element
        }

        int diff = st ^ dr;
    
        // daca st si dr au un mijloc intre ele pe nivelul k
        // inseamna ca st <= segment_start + 2^(k - 1) - 1
        // si dr >= segment_start + 2^(k - 1)
        // segment_start la randul lui este divizibil cu 2^k
        // segment_start = segment_size * i
        // segment_start + 2^(k - 1) - 1 = segment_size * i + (segment_size / 2) - 1
        //                                   \--------> are k de 0 la final in binar
        // ?????????[k de 0] + 1[k - 1 de 0] - 1 = ???????????0[k - 1 de 1]
        // st <= ?????????0[k - 1 de 1]
        // dr >= ?????????[k de 1]
        //         \-------> foarte important, aceste ??????? sunt acelasi prefix si pentru st si pentru dr
        // st = [prefix]0[????]
        // dr = [prefix]1[????]
        // st ^ dr = [prefix]1[????] ^ [prefix]0[????] = [0 de |prefix| ori]1[?????]
        // noi stim ca 1[?????] trebuie sa fie la fel de lung ca sufixul de k de 0
        // deci daca stiu pozitia primului 1 in st ^ dr, stiu si k

        int nivel = 32 - __builtin_clz(diff); // parte intreaga a log2(diff)

        // std::cerr << "query: " << st << " " << dr << std::endl;
        // std::cerr << "nivel: " << nivel << std::endl;

        return mySecret(table[nivel][st], table[nivel][dr]);
    }
} dst;

void Init(int N, int A[]) {
    std::vector<int> v(N);
    for(int i = 0; i < N; i++) {
        v[i] = A[i];
    }
    dst = DisjointSparseTable(v);
}

int Query(int L, int R) {
    return dst.query(L, R);
}
#Verdict Execution timeMemoryGrader output
Fetching results...