Submission #800059

#TimeUsernameProblemLanguageResultExecution timeMemory
800059jakobrsRadio Towers (IOI22_towers)C++17
77 / 100
985 ms26364 KiB
#include <algorithm>
#include <optional>
#include <unordered_set>
#include <vector>
#include <map>
#include <queue>

int n;
std::vector<int> h;

int max_idx;
std::map<int, int> data;

void init(int N, std::vector<int> H) {
    n = N;
    h = H;

    max_idx = 0;
    for (int i = 1; i < N; i++) {
        if (H[i] > H[max_idx]) {
            max_idx = i;
        }
    }

    int towers = 0;

    struct D {
        int prev;
        int max_to_prev;
        int next;
        int max_to_next;
    };
    std::vector<D> adj;

    for (int i = 0; i < N; i++) {
        towers += 1;
        int max_to_prev = H[i];
        int max_to_next = H[i];
        if (i != 0) max_to_prev = std::max(max_to_prev, H[i - 1]);
        if (i + 1 != N) max_to_next = std::max(max_to_next, H[i + 1]);
        adj.push_back({.prev = i - 1,
                       .max_to_prev = max_to_prev,
                       .next = i + 1 == N ? -1 : i + 1,
                       .max_to_next = max_to_next});
    }

    struct PQItem {
        int priority;
        int from;
        int to;

        bool operator<(const PQItem &rhs) const {
            return priority > rhs.priority;
        }
    };

    std::vector<bool> active(adj.size(), true);
    std::priority_queue<PQItem> pq;
    for (int i = 1; i < adj.size(); i++) {
        pq.push({.priority = 0, .from = i - 1, .to = i});
    }

    while (!pq.empty()) {
        auto [priority, from, to] = pq.top();
        pq.pop();

        if (!active[from] || !active[to]) continue;

        data.emplace(priority, towers);

        if (H[from] > H[to]) {
            int max = adj[to].max_to_prev =
                std::max(adj[from].max_to_prev, adj[to].max_to_prev);
            adj[to].prev = adj[from].prev;
            if (adj[to].prev != -1) {
                adj[adj[to].prev].next = to;
                pq.push({.priority = max - std::max(H[adj[to].prev], H[to]),
                         .from = adj[to].prev,
                         .to = to});
            }
            towers -= 1;
            active[from] = false;
        } else {
            int max = adj[from].max_to_next =
                std::max(adj[from].max_to_next, adj[to].max_to_next);
            adj[from].next = adj[to].next;
            if (adj[from].next != -1) {
                adj[adj[from].next].prev = from;
                pq.push({.priority = max - std::max(H[from], H[adj[from].next]),
                         .from = from,
                         .to = adj[from].next});
            }
            towers -= 1;
            active[to] = false;
        }
    }

    data.emplace(2'000'000'000, towers);
}

template <typename Node>
struct SegmentTree {
    std::vector<Node> nodes;
    int offset;

    explicit SegmentTree(int sz) : nodes(2 * sz), offset(sz) {}

    void update(int idx, const Node &node) {
        idx += offset;
        nodes[idx] = node;

        while (idx /= 2) {
            nodes[idx] = nodes[2 * idx] * nodes[2 * idx + 1];
        }
    }

    Node query(int l, int r) const {
        Node left, right;

        l += offset;
        r += offset;

        while (l < r) {
            if (l & 1) left = left * nodes[l++];
            if (r & 1) right = nodes[--r] * right;

            l >>= 1;
            r >>= 1;
        }

        return left * right;
    }
};

int d;

struct LeftNode {
    int a, b, min, max;

    LeftNode() : a(-1), b(-1), min(2'000'000'000), max(-2'000'000'000) {}
    LeftNode(int a) : a(-1), b(-1), min(a), max(a) {}
    LeftNode(int a, int b, int min, int max) : a(a), b(b), min(min), max(max) {}

    LeftNode operator*(const LeftNode &rhs) const {
        std::pair<int, int> c1 = {b, a};
        std::pair<int, int> c2 = {rhs.b, rhs.a};
        std::pair<int, int> c3 = {-1, -1};
        if (rhs.max >= 0 && rhs.max - min >= d) {
            c3 = {rhs.max, min};
        }

        std::pair<int, int> best = std::max(std::max(c1, c2), c3);

        return {best.second, best.first, std::min(min, rhs.min),
                std::max(max, rhs.max)};
    }
};
struct RightNode {
    int a, b, min, max;

    RightNode() : a(-1), b(-1), min(2'000'000'000), max(-2'000'000'000) {}
    RightNode(int a) : a(-1), b(-1), min(a), max(a) {}
    RightNode(int a, int b, int min, int max)
        : a(a), b(b), min(min), max(max) {}

    RightNode operator*(const RightNode &rhs) const {
        std::pair<int, int> c1 = {b, a};
        std::pair<int, int> c2 = {rhs.b, rhs.a};
        std::pair<int, int> c3 = {-1, -1};
        if (max >= 0 && max - rhs.min >= d) {
            c3 = {max, rhs.min};
        }

        std::pair<int, int> best = std::max(std::max(c1, c2), c3);

        return {best.second, best.first, std::min(min, rhs.min),
                std::max(max, rhs.max)};
    }
};

struct PrecomputedData {
    std::unordered_set<int> included_s;
    std::vector<int> prefix_sums, prev, next;

    SegmentTree<LeftNode> left_st;
    SegmentTree<RightNode> right_st;
};

std::optional<PrecomputedData> precomputed;

int max_towers(int L, int R, int D) {
    if (L == R) return 1;
    R += 1;

    if (!precomputed) {
        d = D;
        std::vector<int> included{h[0]};
        int intermediary = h[0];
        for (int i = 1; i < n; i++) {
            if (h[i] > intermediary) {
                intermediary = h[i];
            } else if (intermediary - included.back() < D) {
                if (h[i] < included.back()) {
                    included.pop_back();
                    included.push_back(h[i]);
                    intermediary = h[i];
                }
            } else if (intermediary - h[i] >= D) {
                included.push_back(h[i]);
                intermediary = h[i];
            }
        }

        std::unordered_set<int> included_s(included.begin(), included.end());

        std::vector<int> prev, next;
        int prev_val = -1;
        for (int i = 0; i < n; i++) {
            if (included_s.count(h[i])) {
                prev_val = i;
            }
            prev.push_back(prev_val);
        }
        int next_val = -1;
        for (int i = n - 1; i >= 0; i--) {
            if (included_s.count(h[i])) {
                next_val = i;
            }
            next.push_back(next_val);
        }
        std::reverse(next.begin(), next.end());
        std::vector<int> prefix_sums{1};
        prefix_sums.reserve(n + 1);
        for (int i = 0; i < n; i++) {
            prefix_sums.push_back(prefix_sums.back() + included_s.count(h[i]));
        }

        int o = 1;
        while (o < n) o *= 2;

        SegmentTree<LeftNode> left_st(o);
        SegmentTree<RightNode> right_st(o);

        for (int i = 0; i < n; i++) {
            left_st.update(i, LeftNode(h[i]));
            right_st.update(i, RightNode(h[i]));
        }

        precomputed = std::make_optional(PrecomputedData{
            included_s, prefix_sums, prev, next, left_st, right_st});
    }

    if (L == 0 && R == n) {
        return data.lower_bound(D)->second;
    }

    if (D != d) {
        if (R - 1 <= max_idx) {
            return 1;
        }
        if (L >= max_idx) {
            return 1;
        }

        int a = h[L];
        int b = h[R - 1];
        int c = h[max_idx];

        if (c - a >= D && c - b >= D) {
            return 2;
        } else {
            return 1;
        }
    }

    int total = precomputed->prefix_sums[R] - precomputed->prefix_sums[L];

    int next = precomputed->next[L];
    auto q0 = precomputed->left_st.query(L, next);
    if (q0.b - h[next] >= D) {
        total += 1;
    }

    int prev = precomputed->prev[R - 1];
    auto q1 = precomputed->right_st.query(prev + 1, R);
    if (q1.b - h[prev] >= D) {
        total += 1;
    }

    return std::max(total, 1);
}

Compilation message (stderr)

towers.cpp: In function 'void init(int, std::vector<int>)':
towers.cpp:59:23: warning: comparison of integer expressions of different signedness: 'int' and 'std::vector<init(int, std::vector<int>)::D>::size_type' {aka 'long unsigned int'} [-Wsign-compare]
   59 |     for (int i = 1; i < adj.size(); i++) {
      |                     ~~^~~~~~~~~~~~
#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...