cppAcademy
FROM LEARNING TO COMPETING

Proficient.
Put your skills to the test.

Bulgarian Informatics Group B · 2009–2026

217 independently explained problems: 54 Municipal, 54 Regional and 109 National. Start with the easier tasks and work upwards. Difficulty labels are learning estimates, not official ratings; Group B also includes challenging national-level problems. National-round tasks shared by Groups A and B are included. Years follow Arena’s competition-season labels. Source clarifications and special grader requirements are noted on affected tasks.
GROUP AB2017 · HardRectangle nesting sweep

Rectangular Kingdom

National Olympiad — National round (2017 season)

Submission name: opc.cpp

Problem summary

Rectangle boundaries never touch or cross. Crossing each boundary alternates water and land, with water outside all rectangles. Count the connected land regions.

Source clarification. The PDF’s generic size inequality permits zero width/height, although its island/lake rectangles and nonintersecting contours require positive-area rectangles. The implementation uses that geometric interpretation; zero-area contour semantics are not specified.

Input

N, then rectangle upper-left x y and width height.

Output

Number of land regions.

Constraints

1 ≤ N ≤ 100000; stated coordinate and size bounds 0…10^9; geometric rectangles have positive area.

Worked example

An independently prepared example for this explanation.

Input

9
0 0 16 14
1 1 8 5
2 2 4 3
11 1 3 7
1 7 8 6
2 8 4 4
3 9 2 2
10 9 5 4
11 10 3 2

Output

4

Solution analysis

  1. Disjoint rectangle boundaries imply that rectangles are disjoint or nested. A rectangle at odd nesting depth introduces one land region; one at even depth introduces a lake. Count odd-depth rectangles.
  2. Sweep vertical sides from left to right. Maintain a map from horizontal boundary heights to the depth immediately below each height. At a rectangle’s left side, look up the depth at its top coordinate; its own depth is one greater. Insert its top boundary with the new depth and its bottom boundary with the enclosing depth. At its right side erase those two entries.
  3. The sample nesting includes an outer island, three additional outer-level land subdivisions inside lakes, and lake boundaries that contribute no region. The sweep counts four odd-depth rectangles. Coincident x event coordinates are harmless for disjoint vertical spans; boundary-contact cases are forbidden.

Why it works

At every sweep x, the map partitions the vertical line into depth-constant intervals. Opening a rectangle increases depth by one exactly between its top and bottom, while closing restores the enclosing interval. Because no boundaries cross, active descendants cannot extend beyond a closing ancestor. Thus every recorded depth is its actual nesting depth. Alternating land and water makes each odd-depth rectangle one distinct connected land component.

Complexity: O(N log N) time and O(N) space.

Watch out. Counting all rectangles or only outermost rectangles misses islands inside lakes. Endpoint coordinates x+width may reach 2·10^9, so 64-bit coordinates are convenient.

Commented C++17 solution

An independently written complete program with standard headers.

#include<iostream>
#include<vector>
#include<map>
#include<algorithm>
// Algorithm: Disjoint rectangle boundaries imply that rectangles are disjoint or nested.
using namespace std;
struct Event{
    long long x,top,bottom;
    int type;
    bool operator<(const Event&other)const{
        if(x!=other.x){
            return x<other.x;
        }
        return type<other.type;
    }
};
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin>>n;
    vector<Event>events;
    for(int i=0;i<n;++i){
        long long x,y,width,height;
        cin>>x>>y>>width>>height;
        events.push_back({x,y,y+height,1});
        events.push_back({x+width,y,y+height,-1});
    }
    sort(events.begin(),events.end());
    map<long long,int>depth;
    depth[-1]=0;
    int answer=0;
    for(auto event:events){
        if(event.type==-1){
            depth.erase(event.top);
            depth.erase(event.bottom);
        }
        else{
            int enclosing=prev(depth.upper_bound(event.top))->second;
            int current=enclosing+1;
            if(current%2==1){
                ++answer;
            }
            depth[event.top]=current;
            depth[event.bottom]=enclosing;
        }
    }
    cout<<answer<<'\n';
}

Source: Arena Olimpiici ↗