Submission #1303907

#TimeUsernameProblemLanguageResultExecution timeMemory
1303907chaitanyamehtaCurtains (NOI23_curtains)C++20
60 / 100
1603 ms362712 KiB
// Compile with: g++ -std=c++17 -O2 curtains_subtask5.cpp -o curtains
#include <bits/stdc++.h>
using namespace std;
using pii = pair<int,int>;
using vi = vector<int>;
struct Node {
    vector<pair<int,int>> intervals; // (l, r) sorted by r
    vector<int> prefix_hole;  // size = intervals.size(), hole values
    vector<int> prefix_reach; // size = intervals.size(), reach values
};

struct SegTree {
    int n;
    vector<Node> st;
    vector<vector<pair<int,int>>> start_at; // intervals starting at position i

    SegTree(int _n=0){ init(_n); }
    void init(int _n){
        n = _n;
        st.assign(4*n+5, Node());
        start_at.assign(n+2, {});
    }

    void add_interval_input(int l, int r){
        if(l<1 || l>n) return;
        start_at[l].push_back({l,r});
    }

    // build: merge children intervals and compute prefix arrays per node
    void build_all(int idx, int L, int R){
        if(L==R){
            auto &node = st[idx];
            node.intervals = start_at[L];
            sort(node.intervals.begin(), node.intervals.end(), [](auto &a, auto &b){
                if(a.second != b.second) return a.second < b.second;
                return a.first < b.first;
            });
        } else {
            int mid=(L+R)/2;
            build_all(idx*2, L, mid);
            build_all(idx*2+1, mid+1, R);
            // merge child intervals by r
            auto &left = st[idx*2].intervals;
            auto &right = st[idx*2+1].intervals;
            auto &nodeint = st[idx].intervals;
            nodeint.clear();
            nodeint.reserve(left.size() + right.size());
            std::merge(left.begin(), left.end(), right.begin(), right.end(),
                       back_inserter(nodeint),
                       [](const pair<int,int>&a, const pair<int,int>&b){
                           if(a.second != b.second) return a.second < b.second;
                           return a.first < b.first;
                       });
        }
        // compute prefix arrays for this node (DSU marks inside [L,R])
        auto &node = st[idx];
        int m = (int)node.intervals.size();
        node.prefix_hole.assign(m, 0);
        node.prefix_reach.assign(m, 0);
        if(m == 0) return; // nothing to do
        int len = R - L + 1;
        // DSU parent: parent[i] = largest uncovered index <= i (1-based within [L,R])
        vector<int> parent(len+1);
        for(int i=0;i<=len;i++) parent[i] = i;
        function<int(int)> findp = [&](int x)->int{
            if(x<=0) return 0;
            return parent[x]==x?x:parent[x]=findp(parent[x]);
        };
        int reach = R; // for empty prefix, reach = R (no extension beyond R)
        for(int i=0;i<m;i++){
            int l = node.intervals[i].first;
            int r = node.intervals[i].second;
            // update reach (furthest right covered by prefix)
            reach = max(reach, r);
            // mark covered positions inside [L,R]: cover [u,v]
            int u = max(l, L);
            int v = min(r, R);
            if(u <= v){
                int pos = findp(v - L + 1);
                while(pos > 0 && (L + pos - 1) >= u){
                    // mark pos as covered
                    parent[pos] = pos - 1;
                    pos = findp(pos);
                }
            }
            // compute hole: largest index inside [L,R] that is uncovered
            int last_uncovered = findp(len); // index in 1..len or 0
            int hole;
            if(last_uncovered == 0) hole = L - 1; // no hole inside
            else hole = L + last_uncovered - 1;
            node.prefix_hole[i] = hole;
            node.prefix_reach[i] = reach;
        }
    }

    // collect nodes fully inside [ql,qr]
    void collect_nodes(int idx, int L, int R, int ql, int qr, vector<tuple<int,int,int>> &out){
        if(ql > R || qr < L) return;
        if(ql <= L && R <= qr){
            out.emplace_back(idx, L, R);
            return;
        }
        int mid=(L+R)/2;
        collect_nodes(idx*2, L, mid, ql, qr, out);
        collect_nodes(idx*2+1, mid+1, R, ql, qr, out);
    }

    // combine two adjacent segments:
    // left covers [a,b], right covers [b+1,c]
    // left: (hole1, reach1), right: (hole2, reach2)
    // hole values use sentinel: hole = interval.L - 1 means no hole in that interval
    pair<int,int> combine_adjacent(const pair<int,int> &left, const pair<int,int> &right,
                                   int a, int b, int c){
        int hole1 = left.first, reach1 = left.second;
        int hole2 = right.first, reach2 = right.second;
        int hole, reach;
        if(hole1 >= a){
            // left has a hole inside [a,b] -> cannot be fixed by right
            hole = hole1;
        } else {
            // left has no hole inside [a,b]
            if(hole2 >= b+1){
                // right has a hole in [b+1,c]; left can fix it only if reach1 >= hole2
                if(reach1 >= hole2){
                    hole = a - 1; // fixed -> no hole
                } else {
                    hole = hole2;
                }
            } else {
                // right has no hole -> no hole overall
                hole = a - 1;
            }
        }
        reach = max(reach1, reach2);
        return {hole, reach};
    }

    // query: returns true if [s,e] can be exactly covered
    bool query(int s, int e){
        vector<tuple<int,int,int>> segs;
        collect_nodes(1,1,n,s,e,segs);
        if(segs.empty()) return false;
        // sort nodes left->right by their L
        sort(segs.begin(), segs.end(), [](auto &A, auto &B){
            return get<1>(A) < get<1>(B);
        });
        bool started = false;
        pair<int,int> cur; int curL=0, curR=0;
        for(auto &t : segs){
            int idx = get<0>(t), L = get<1>(t), R = get<2>(t);
            auto &node = st[idx];
            // find count of curtains with r <= e (binary search)
            int cnt = 0;
            if(!node.intervals.empty()){
                // node.intervals sorted by r
                int lo = 0, hi = (int)node.intervals.size(); // count = first index with r > e
                while(lo < hi){
                    int mid = (lo + hi) >> 1;
                    if(node.intervals[mid].second <= e) lo = mid + 1;
                    else hi = mid;
                }
                cnt = lo;
            }
            pair<int,int> nodeRes;
            if(cnt == 0){
                // empty prefix: hole = R (largest uncovered inside), reach = R (no extension)
                nodeRes = { R, R };
            } else {
                nodeRes = { node.prefix_hole[cnt-1], node.prefix_reach[cnt-1] };
            }
            if(!started){
                cur = nodeRes;
                curL = L; curR = R;
                started = true;
            } else {
                cur = combine_adjacent(cur, nodeRes, curL, curR, R);
                curR = R;
            }
        }
        // final check: if final hole < s then no hole inside [s,e]
        int final_hole = cur.first;
        return !(final_hole >= s && final_hole <= e);
    }
};


int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
#if 0
    // Example I/O format (replace with your problem format)
    // n m q
    // m lines: l r
    // q lines: s e
#endif
    int n, m, q;
    if(!(cin >> n >> m >> q)) return 0;
    SegTree st(n);
    for(int i=0;i<m;i++){
        int l,r; cin>>l>>r;
        if(l<1) l=1;
        if(r>n) r=n;
        if(l<=n && r>=1 && l<=r) st.add_interval_input(l,r);
    }
    st.build_all(1,1,n);
    while(q--){
        int s,e; cin >> s >> e;
        if(s<1) s=1;
        if(e>n) e=n;
        if(s>e){
            cout << "NO\n";
            continue;
        }
        bool ok = st.query(s,e);
        cout << (ok? "YES\n" : "NO\n");
    }
    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...