직사각형 합집합 면적 구하기
기본적으로 각 직사각형의 꼭짓점이 되는 점을 x좌표 기준으로 스위핑하면서 각 점 사이의 사각형 넓이를 구하는 식으로 답을 구한다. 위 그림과 같이 두 직사각형이 주어지면, 노랑, 빨강, 파랑에 해당하는 사각형의 넓이를 각각 구한 후 더하는 것이다. 이를 위해서
- 우선 각 직사각형의 x 시작 좌표, x 끝 좌표와 y 시작 및 끝을 저장해놓는다. 그리고 x를 기준으로 정렬한다.
- cnt라는 이름의 세그먼트 트리에 y 범위에 대해 x 시작 좌표에서 +1, x 끝 좌표에서 -1을 해준다.
- cnt가 1 이상이면 (직사각형이 있는 구간이면) tree라는 이름의 세그먼트 트리에 직사각형이 있는 y 구간의 총 높이를 저장한다.
- 각 점 사이의 x 너비와 y 구간의 총 높이(
tree[1]
)를 곱해서 각 점 사이의 사각형 넓이를 구한다.
#include <iostream>#include <vector>#include <algorithm>#define MAX 30001using namespace std;
struct S { int x, y1, y2, t;};
int tree[MAX*4], cnt[MAX*4];
void update(int x, int s, int e, int gs, int ge, int d) { if (e<gs||ge<s) return; if (gs<=s&&e<=ge) { cnt[x]+=d; } else { int mid=(s+e)/2; update(2*x, s, mid, gs, ge, d); update(2*x+1, mid+1, e, gs, ge, d); }
if (cnt[x]>0) { tree[x]=e-s+1; } else { if (s==e) tree[x]=0; else tree[x]=tree[x*2]+tree[x*2+1]; }}
int main() { vector<S> V; int n; cin>>n; for (int i=0;i<n;i++) { int x1, y1, x2, y2; cin>>x1>>y1>>x2>>y2; V.push_back({ x1, y1, y2-1, 1 }); V.push_back({ x2, y1, y2-1, -1 }); } sort(V.begin(), V.end(), [](S &a, S &b) { return a.x < b.x; }); int res=0; for (int i=0;i<V.size();i++) { if (i) res += (tree[1]*(V[i].x-V[i-1].x)); update(1, 0, MAX, V[i].y1, V[i].y2, V[i].t); } cout<<res;}
이 문제는 위 문제보다 좌표의 범위가 넓기 떄문에(0~10^9), 좌표를 기준으로 트리를 생성하면 메모리가 초과된다.
따라서 y의 인덱스를 기준으로 트리를 만들어 계산하면 된다. (좌표 압축)
#include <iostream>#include <vector>#include <algorithm>#include <set>#define MAX 400001using namespace std;
struct S { long long x, y1, y2, t;};
vector<long long> y;long long seg[MAX*4];long long cnt[MAX*4];
void update(long long x, long long s, long long e, long long gs, long long ge, long long d) { if (e<gs||ge<s) return; if (gs<=s&&e<=ge) { cnt[x]+=d; } else { long long mid=(s+e)/2; update(2*x, s, mid, gs, ge, d); update(2*x+1, mid+1, e, gs, ge, d); }
if (cnt[x]>0) { seg[x]=y[e]-y[s-1]; } else { if (s==e) seg[x]=0; else seg[x]=seg[x*2]+seg[x*2+1]; }}
int main() { set<long long> yset; vector<S> V; long long n; cin>>n; for (long long i=0;i<n;i++) { long long x1, y1, x2, y2; cin>>x1>>x2>>y1>>y2; yset.insert(y1); yset.insert(y2); V.push_back({ x1, y1, y2, 1 }); V.push_back({ x2, y1, y2, -1 }); } y.resize(yset.size()); std::copy(yset.begin(), yset.end(), y.begin()); sort(V.begin(), V.end(), [](S &a, S &b) { return a.x < b.x; }); long long res=0; for (long long i=0;i<V.size();i++) { if (i) res += (seg[1]*(V[i].x-V[i-1].x)); long long idx1 = lower_bound(y.begin(), y.end(), V[i].y1) - y.begin(); long long idx2 = lower_bound(y.begin(), y.end(), V[i].y2) - y.begin(); update(1, 0, MAX, idx1+1, idx2, V[i].t); } cout<<res;}
직사각형 합집합 둘레 구하기
직사각형 합집합의 둘레를 구하는 문제도 비슷한 방식의 스위핑으로 해결할 수 있다.
y 구간의 총 높이를 저장하는 것은 똑같은데, 총 높이가 이전에 비해 변한만큼 둘레로 더해주면 된다.
x,y 둘레를 더해야하기 때문에 x에서 y1, y2까지, y에서 x1, x2까지를 각각 탐색하면서 값에 더해준다.
#include <iostream>#include <vector>#include <algorithm>#include <string.h>#define MAX 30001using namespace std;
struct S { int x, y1, y2, t;};
int seg[MAX*4], cnt[MAX*4];
void update(int x, int s, int e, int gs, int ge, int d) { if (e<gs||ge<s) return; if (gs<=s&&e<=ge) { cnt[x]+=d; } else { int mid=(s+e)/2; update(2*x, s, mid, gs, ge, d); update(2*x+1, mid+1, e, gs, ge, d); }
if (cnt[x]>0) { seg[x]=e-s+1; } else { if (s==e) seg[x]=0; else seg[x]=seg[x*2]+seg[x*2+1]; }}
int main() { vector<S> V, V2; int n; cin>>n; for (int i=0;i<n;i++) { int x1, y1, x2, y2; cin>>x1>>y1>>x2>>y2; if (x2<x1) swap(x1,x2); if (y2<y1) swap(y1,y2); x1+=10000, y1+=10000, x2+=10000, y2+=10000; V.push_back({ x1, y1, y2-1, 1 }); V.push_back({ x2, y1, y2-1, -1 }); V2.push_back({ y1, x1, x2-1, 1 }); V2.push_back({ y2, x1, x2-1, -1 }); } sort(V.begin(), V.end(), [](S &a, S &b) { if (a.x==b.x) return a.t > b.t; else return a.x < b.x; }); sort(V2.begin(), V2.end(), [](S &a, S &b) { if (a.x==b.x) return a.t > b.t; else return a.x < b.x; });
int res=0, last=0; for (int i=0;i<V.size();i++) { update(1, 0, MAX, V[i].y1, V[i].y2, V[i].t); res+=abs(seg[1]-last); last=seg[1]; } memset(seg, 0, sizeof(seg)); last=0; for (int i=0;i<V2.size();i++) { update(1, 0, MAX, V2[i].y1, V2[i].y2, V2[i].t); res+=abs(seg[1]-last); last=seg[1]; } cout<<res;}