※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※

 

이번에 볼 문제는 백준 3351번 문제인 삼각 분할이다.
문제는 아래 링크를 확인하자.

https://www.acmicpc.net/problem/3351 

 

3351번: 삼각 분할

계산기하 분야에서 삼각분할은, 다음과 같은 조건을 만족하는 삼각형의 집합을 뜻한다. 삼각형의 각 꼭짓점은 다각형에 있는 꼭짓점 중 하나이다. 삼각형끼리는 겹쳐서 안되며, 삼각형들의 합

www.acmicpc.net

볼록다각형의 삼각분할이 주어질 때, 각 삼각형을 노드로 하고 변을 공유하는 삼각형들의 노드 사이에 에지를 그으면 그 그래프는 트리가 됨을 알 수 있다. (사이클이 존재한다면, 공유하는 하나의 꼭짓점이 존재하는 사이클이 존재하고 이는 볼록다각형의 꼭짓점만을 사용한 분할이 아니게 되기 때문이다.)

이 트리를 구현하고, 같은 색을 가진 꼭짓점 사이에 있는 에지를 모두 "자를 수 없는 에지"로 표시하여 문제를 해결할 수 있다. 글쓴이는 이를 HLD(heavy-light decomposition)와 지연전파(lazy propagation)를 이용한 세그먼트 트리(segment tree)로 해결하였다.

 

HLD를 이용하여 에지를 관리할 때 루트방향이 어느 쪽인지 혼동하는 실수를 줄여야하는데...

 

아래는 제출한 소스코드이다.

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

vector<int> G[100001];
vector<int> colors[100001];
vector <pair<pair<int, int>, int>> edges;

int heavychild[100001];
int dfs(int current, int parent) {
	int childcnt = 1;
	int hchild = -1;
	int hcount = -1;
	for (auto node : G[current]) {
		if (node == parent) continue;
		int temp = dfs(node, current);
		childcnt += temp;
		if (temp > hcount) {
			hcount = temp;
			hchild = node;
		}
	}
	heavychild[current] = hchild;
	return childcnt;
}

int nodeidx[100001];
int chainidx[100001];
int cnth[100001];
int cparent[100001];
int cdepth[100001];

int nidx = 1, cidx = 1;
void hld(int current, int parent, int cn, int cp, int cd) {
	nodeidx[current] = nidx;
	chainidx[nidx] = cidx;
	cnth[nidx] = cn;
	cparent[nidx] = cp;
	cdepth[nidx] = cd;

	int hchild = heavychild[current];
	if (hchild > 0) {
		nidx++;
		hld(hchild, current, cn + 1, cp, cd);
	}
	cd++;
	for (auto node : G[current]) {
		if (node == parent || node == hchild) continue;
		nidx++; cidx++;
		hld(node, current, 0, nodeidx[current], cd);
	}
}

int seg[262145];
bool lazy[262145];

void propagate(int L, int R, int sI) {
	seg[sI] = (R - L + 1);
	if (L < R) {
		lazy[sI * 2] = 1;
		lazy[sI * 2 + 1] = 1;
	}
	lazy[sI] = 0;
}

int update(int L, int R, int qL, int qR, int sI) {
	if (lazy[sI]) propagate(L, R, sI);
	if (R < qL || qR < L) return seg[sI];
	if (qL <= L && R <= qR) {
		lazy[sI] = 1;
		propagate(L, R, sI);
		return seg[sI];
	}
	return seg[sI] = update(L, (L + R) / 2, qL, qR, sI * 2) + update((L + R) / 2 + 1, R, qL, qR, sI * 2 + 1);
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);

	int N; cin >> N;
	for (int i = 3; i <= N; i++) {
		int a, b, c, d; cin >> a >> b >> c >> d;
		
		colors[d].push_back(i);

		if (a < b) edges.push_back({ {a,b},i });
		else edges.push_back({ {b,a},i });
		
		if (b < c) edges.push_back({ {b,c},i });
		else edges.push_back({ {c,b},i });
		
		if (c < a) edges.push_back({ {c,a},i });
		else edges.push_back({ {a,c},i });
	}
	sort(edges.begin(), edges.end());
	int esize = edges.size();
	for (int i = 1; i < esize; i++) {
		if ((edges[i - 1].first.first == edges[i].first.first) && (edges[i - 1].first.second == edges[i].first.second)) {
			int x = edges[i - 1].second, y = edges[i].second;
			G[x].push_back(y);
			G[y].push_back(x);
		}
	}

	dfs(N, 0);
	hld(N, 0, 0, 0, 0);

	for (int i = 1; i <= N; i++) {
		int cnt = colors[i].size();
		for (int k = 1; k < cnt; k++) {
			int x = nodeidx[colors[i][k - 1]], y = nodeidx[colors[i][k]];
			if (cdepth[x] != cdepth[y]) {
				if (cdepth[x] < cdepth[y]) swap(x, y);
				while (cdepth[x] > cdepth[y]) {
					update(1, N, x - cnth[x], x, 1);
					x = cparent[x];
				}
			}
			while (chainidx[x] != chainidx[y]) {
				update(1, N, x - cnth[x], x, 1);
				update(1, N, y - cnth[y], y, 1);
				x = cparent[x], y = cparent[y];
			}
			if (x > y) swap(x, y);
			if (x < y) update(1, N, x + 1, y, 1);
		}
	}

	cout << N - 3 - seg[1];
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 1647 // C++] 도시 분할 계획  (0) 2021.07.21
[BOJ 15480 // C++] LCA와 쿼리  (0) 2021.07.20
[BOJ 2570 // C++] 비숍2  (0) 2021.07.18
[BOJ 5398 // C++] 틀렸습니다  (0) 2021.07.17
[BOJ 1574 // C++] 룩 어택  (0) 2021.07.16

+ Recent posts