읽는데 약 2분
[백준 3392] 화성지도
문제
알고리즘
Sweeping
풀이
사각형을 구성하는 좌표들이 주어질 때, 모든 사각형의 면적을 구하는 문제입니다. 단 겹치는 좌표는 한 번만 계산해야 합니다.
각 직사각형을 두 개의 선분으로 분리한 후, x좌표를 기준으로 정렬합니다. 두개의 선분이란 직사각형을 놓았을 때 가장 왼쪽에 있는 선분, 가장 오른쪽에 있는 선분을 의미합니다. 예를 들어 직사각형을 이루는 좌표 (10,10), (20,20)을 받았다면
두 직선은 (10,10,20), (20,10,20)입니다. 앞에서부터 x좌표와 y좌표들입니다.
그리고 순차적으로 각 간선들을 살펴나가며 만약 왼쪽에 있는 선분이라면 그 선분이 가리키는 y좌표들을 업데이트해주고, 오른쪽에 있는 선분이라면 그 선분이 가리키는 y좌표를 다시 제거해줍니다. 왼쪽 선분은 이제부터 고려해야 하는 직사각형의 등장을 의미하고 오른쪽 선분은 해당 직사각형에 대한 고려가 끝났다는 것과 동치입니다. 이와 동시에 (현재 x좌표-이전에 처리한 x좌표)*(현재 업데이트되어있는 y좌표들의 합) 을 통해 매 순간 겹치지 않게 직사각형들을 구해나갈 수 있습니다.
남은 문제는 현재 업데이트 되어있는 y좌표들을 구하는 것입니다. 세그먼트 트리에서 구간 업데이트를 통해 업데이트된 y좌표들을 관리해나갈 수 있으며, 업데이트된 y좌표들의 길이를 구하기 위해 새로운 세그먼트 트리를 하나 더 사용합니다.
문제에서 유의할 점 중 하나는 업데이트를 할 때, y2의 좌표를 하나 감소시켜주는 것입니다. 이는 y좌표가 나타내는 것이 y~y+1까지의 구간을 나타내기 때문입니다. 시간 복잡도는 O(NlogN)입니다.
코드
#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<n;++i)
#define REP(i,n) for(int i=1;i<=n;++i)
#define FAST cin.tie(NULL);cout.tie(NULL); ios::sync_with_stdio(false)
using namespace std;
typedef tuple<int, int, int, int> tp;
const int MAXN = 30000 + 2;
int N, ans;
int cnt[MAXN << 2], tree[MAXN << 2];
vector<tp> edge;
void update(int node, int nl, int nr, int ql, int qr, int val) {
if (qr < nl || nr < ql) return;
if (ql <= nl && nr <= qr) tree[node] += val;
else {
int mid = (nl + nr) / 2;
update(node * 2, nl, mid, ql, qr, val);
update(node * 2 + 1, mid + 1, nr, ql, qr, val);
}
cnt[node] = tree[node] ? nr - nl + 1 : nl ^ nr ? cnt[node * 2] + cnt[node * 2 + 1] : 0;
}
int main() {
FAST;
cin >> N;
rep(i, N) {
int a, b, c, d;
cin >> a >> b >> c >> d;
edge.emplace_back(a, b, d, 1);
edge.emplace_back(c, b, d, -1);
}
sort(edge.begin(), edge.end());
int prv = 0;
rep(i, 2 * N) {
auto [x, y1, y2, val] = edge[i];
if (i) ans += (x - prv) * cnt[1];
prv = x;
update(1, 0, MAXN, y1, y2 - 1, val);
}
cout << ans;
return 0;
}