Submission #1261495

#TimeUsernameProblemLanguageResultExecution timeMemory
1261495liangjeremyCurtains (NOI23_curtains)C++20
20 / 100
193 ms11856 KiB
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>

// Fast I/O
void setup_io() {
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(NULL);
}

//--- Solution for Subtask 3 (and all smaller subtasks) ---//
// Algorithm: Dynamic Programming / Greedy Precomputation
// Complexity: O(n^2 + m + q)
// This approach is suitable for n <= 2000.
void solve_subtask_3() {
    int n, m, q;
    std::cin >> n >> m >> q;

    // 1. Pre-calculate max endpoint 'r' for each start point 'l'
    std::vector<int> max_r_at(n + 1, 0);
    for (int i = 0; i < m; ++i) {
        int l, r;
        std::cin >> l >> r;
        max_r_at[l] = std::max(max_r_at[l], r);
    }

    // 2. Precompute all possible [s, e] ranges
    // possible[s][e] will be true if the range [s, e] can be exactly covered.
    std::vector<std::vector<bool>> possible(n + 1, std::vector<bool>(n + 1, false));

    for (int s = 1; s <= n; ++s) {
        int max_reach = s - 1; // Tracks the farthest point covered in a block starting at 's'
        for (int i = s; i <= n; ++i) {
            // If the current position 'i' is adjacent to our covered block
            if (i <= max_reach + 1) {
                // We can potentially extend our reach using a curtain starting at 'i'
                max_reach = std::max(max_reach, max_r_at[i]);
            }
            
            // If our contiguous block now covers all sections up to 'i'
            if (max_reach >= i) {
                possible[s][i] = true;
            }
        }
    }

    // 3. Answer queries in O(1) each
    for (int j = 0; j < q; ++j) {
        int s, e;
        std::cin >> s >> e;
        if (possible[s][e]) {
            std::cout << "YES\n";
        } else {
            std::cout << "NO\n";
        }
    }
}


//--- Solution for Subtask 4 (s[j] = 1) ---//
// Algorithm: Offline processing with Disjoint Set Union (DSU)
// Complexity: O((m + q) * alpha(n))
// This is a highly efficient approach for the specific case where all queries start at 1.

// DSU data structure
struct DSU {
    std::vector<int> parent;
    DSU(int n) {
        parent.resize(n + 1);
        // Initially, the furthest reach from i is just i itself
        std::iota(parent.begin(), parent.end(), 0); 
    }

    // Find the representative (furthest reach) with path compression
    int find(int i) {
        if (parent[i] == i) {
            return i;
        }
        return parent[i] = find(parent[i]);
    }

    // Union two sets
    void unite(int i, int j) {
        int root_i = find(i);
        int root_j = find(j);
        if (root_i != root_j) {
            // The new reach is the maximum of the two
            if (root_i > root_j) {
                parent[root_j] = root_i;
            } else {
                parent[root_i] = root_j;
            }
        }
    }
};

struct Curtain {
    int l, r;
};

struct Query {
    int e, id;
};

void solve_subtask_4() {
    int n, m, q;
    std::cin >> n >> m >> q;

    std::vector<Curtain> curtains(m);
    for (int i = 0; i < m; ++i) {
        std::cin >> curtains[i].l >> curtains[i].r;
    }

    std::vector<Query> queries(q);
    for (int i = 0; i < q; ++i) {
        int s; // s is always 1, but we must read it
        std::cin >> s >> queries[i].e;
        queries[i].id = i;
    }

    // 1. Sort curtains and queries by their right endpoint
    std::sort(curtains.begin(), curtains.end(), [](const Curtain& a, const Curtain& b) {
        return a.r < b.r;
    });
    std::sort(queries.begin(), queries.end(), [](const Query& a, const Query& b) {
        return a.e < b.e;
    });

    std::vector<bool> ans(q);
    DSU dsu(n);
    int curtain_idx = 0;

    // 2. Process queries in sorted order
    for (const auto& query : queries) {
        // Add all curtains that are now available (r <= current query's e)
        while (curtain_idx < m && curtains[curtain_idx].r <= query.e) {
            int l = curtains[curtain_idx].l;
            int r = curtains[curtain_idx].r;
            
            // This curtain creates a bridge from l-1 to r.
            // We merge all reachable segments from l-1 onwards that are less than r.
            // This loop is efficient because `pos` always jumps over an entire merged set.
            for (int pos = dsu.find(l - 1); pos < r; pos = dsu.find(pos + 1)) {
                 dsu.unite(pos, r);
            }
            curtain_idx++;
        }

        // 3. Check if we can cover [1, e]
        // The DSU root of 0 (representing before section 1) tells us the max reach from the start.
        ans[query.id] = (dsu.find(0) == query.e);
    }
    
    // 4. Print answers in original order
    for (int i = 0; i < q; ++i) {
        if (ans[i]) {
            std::cout << "YES\n";
        } else {
            std::cout << "NO\n";
        }
    }
}

int main() {
    setup_io();

    // To run the code for a specific subtask, call the corresponding function.
    // For the general case (Subtask 3 and below):
    // solve_subtask_3();
    
    // For the special case s[j]=1 (Subtask 4):
    solve_subtask_4();

    return 0;
}
#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...