Sweep Line: Algorithm, Mathematics, Complexity, and Applications
1. Algorithm Overview
The sweep line (or line-sweep) technique “sweeps” a line (usually vertical) across the plane, processing events in sorted order and maintaining an active set of objects (intervals, segments, points) in a data structure (balanced BST, multiset, segment tree…).
Typical steps:
- Generate events for each object (e.g. for an interval [l,r]: “enter” at x=l, “exit” at x=r).
- Sort all events by x-coordinate (breaking ties: “enter” before “exit”).
- Maintain an active set of objects—insert on “enter”, remove on “exit”.
- At each event you can query the active set to compute answers (e.g. maximum overlap, nearest neighbor, area contribution, etc.).
Time Complexity
- Sorting E events:
O(E log E)
. - Each insertion/removal/query in active structure: typically
O(log E)
.
Total: O(E log E)
.
Memory Complexity
Storing events and active structure of size ≤ E: O(E).
2. Mathematics of Sweep Line
- Event ordering: ensure correctness by sorting events by x, tie-breaking enters before exits so intervals of zero length are handled.
- Active set invariants: at sweep-line position x, the active set contains exactly those objects intersecting the vertical line x=const.
- Area by integration: when computing union of rectangles, track the total covered length L(x) at each x, then area = ∫ L(x) dx ≈ Σ L(x_i)·(x_{i+1}−x_i).
3. Easy Problems
3.1 – Codeforces 610D “Points on a Line”
Given N vertical intervals [l_i, r_i] on the x-axis, find the maximum number of intervals that overlap at any point.
Input: 5 1 4 2 5 3 6 5 7 6 8 Output: 3
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
int N; cin >> N;
vector<pair<int,int>> events;
for(int i=0;i<N;i++){
int l,r; cin>>l>>r;
events.emplace_back(l, +1); // enter
events.emplace_back(r, -1); // exit
}
sort(events.begin(), events.end(),
[](auto &a, auto &b){
if(a.first!=b.first) return a.first<b.first;
return a.second>b.second; // enter before exit
});
int cur=0, best=0;
for(auto &e: events){
cur += e.second;
best = max(best, cur);
}
cout << best << "\n";
return 0;
}
3.2 – Codeforces 1117C “Union of Segments Length”
Given N intervals [l_i,r_i], compute the total length covered by their union. Use sweep-line + maintain current coverage count and last x.
Input: 3 1 3 2 5 6 8 Output: 5
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
int N; cin>>N;
vector<pair<int,int>> ev;
for(int i=0;i<N;i++){
int l,r; cin>>l>>r;
ev.emplace_back(l, +1);
ev.emplace_back(r, -1);
}
sort(ev.begin(), ev.end());
int count=0;
int lastX = ev[0].first;
long long total=0;
for(auto &e: ev){
int x=e.first, delta=e.second;
if(count>0) total += x - lastX;
count += delta;
lastX = x;
}
cout << total << "\n";
return 0;
}
4. Intermediate Problems
4.1 – Codeforces 1462F “The Treasure of The Segments”
Given N segments [l_i,r_i] and an integer K, find the maximum possible sum of lengths of K non-overlapping segments chosen from the set. Approach: sweep-line + a multiset of lengths of “active” segments, always keep only top K.
Input: 5 2 1 5 2 6 4 7 8 10 9 11 Output: 8
#include <iostream>
#include <vector>
#include <algorithm>
#include <set>
using namespace std;
using ll = long long;
int main(){
int N,K; cin>>N>>K;
vector<pair<int,pair<int,int>>> ev;
for(int i=0;i<N;i++){
int l,r; cin>>l>>r;
ev.push_back({l, {+1, r-l}});
ev.push_back({r, {-1, r-l}});
}
sort(ev.begin(), ev.end());
multiset<int> active;
ll sum = 0, best = 0;
for(auto &e: ev){
int type = e.second.first, len = e.second.second;
if(type == +1){
active.insert(len);
sum += len;
if(active.size() > K){
auto it = prev(active.end());
sum -= *it;
active.erase(it);
}
} else {
// removal: erase one instance if present
auto it = active.find(len);
if(it != active.end()){
sum -= len;
active.erase(it);
}
}
best = max(best, sum);
}
cout << best << "\n";
return 0;
}
4.2 – Codeforces 920E “Connected Components in a Dynamic Graph”
We have N nodes on a line, and M operations adding/removing an edge between consecutive nodes. After each operation, report number of connected components. Sweep over operations in time, maintain a BIT counting active edges; components = N − active_edges.
Input: 5 4 + 2 + 4 - 2 + 3 Output: 4 3 4 3
#include <iiostream>
#include <vector>
#include <string>
using namespace std;
int main(){
int N, M; cin >> N >> M;
vector<bool> active(N,false);
int activeEdges = 0;
while(M--){
char op; int x;
cin >> op >> x; // edge between x and x+1
if(op == '+'){
if(!active[x]){
active[x]=true;
activeEdges++;
}
} else {
if(active[x]){
active[x]=false;
activeEdges--;
}
}
cout << (N - activeEdges) << "\n";
}
return 0;
}
5. Hard Problem
5.1 – Codeforces 1199F “Rectangle Area Union”
Given N axis-aligned rectangles, compute the total area of their union. Classic sweep-line:
- Events at x = left (add [y1,y2]), x = right (remove [y1,y2]).
- Maintain a segment tree over y-coordinates storing covered length.
- Sum area += covered_length * (x_{i+1}−x_i).
Input: 3 1 1 4 3 2 2 5 5 3 0 6 2 Output: 14
#include <iiostream>
#include <vector>
#include <algorithm>
using namespace std;
using ll = long long;
struct Event {
int x, y1, y2, type;
bool operator<(const Event&o) const {
return x < o.x;
}
};
struct SegTree {
int n;
vector<int> cnt;
vector<ll> len;
vector<int> ys;
SegTree(const vector<int>& ys_): ys(ys_) {
n = ys.size()-1;
cnt.assign(4*n, 0);
len.assign(4*n, 0);
}
void update(int idx,int l,int r,int ql,int qr,int v){
if(ql>r || qr<l) return;
if(ql<=l && r<=qr){
cnt[idx]+=v;
} else {
int m=(l+r)/2;
update(idx*2,l,m,ql,qr,v);
update(idx*2+1,m+1,r,ql,qr,v);
}
if(cnt[idx]>0) len[idx]=ys[r+1]-ys[l];
else if(l==r) len[idx]=0;
else len[idx]=len[idx*2]+len[idx*2+1];
}
};
int main(){
int N; cin>>N;
vector<Event> ev;
vector<int> Y;
for(int i=0;i<N;i++){
int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
ev.push_back({x1,y1,y2, +1});
ev.push_back({x2,y1,y2, -1});
Y.push_back(y1);
Y.push_back(y2);
}
sort(Y.begin(),Y.end());
Y.erase(unique(Y.begin(),Y.end()),Y.end());
sort(ev.begin(),ev.end());
SegTree st(Y);
ll area=0;
for(int i=0;i+1<ev.size();i++){
int y1 = lower_bound(Y.begin(),Y.end(), ev[i].y1) - Y.begin();
int y2 = lower_bound(Y.begin(),Y.end(), ev[i].y2) - Y.begin() - 1;
st.update(1,0,st.n-1,y1,y2, ev[i].type);
ll width = ev[i+1].x - ev[i].x;
area += st.len[1] * width;
}
cout<<area<<"\n";
return 0;
}